]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_upstream.c
snapcraft: fix minor typo
[mirror_frr.git] / pimd / pim_upstream.c
CommitLineData
12e41d03
DL
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
12e41d03
DL
19*/
20
21#include <zebra.h>
22
23#include "zebra/rib.h"
24
25#include "log.h"
26#include "zclient.h"
27#include "memory.h"
28#include "thread.h"
29#include "linklist.h"
dfe43e25
DW
30#include "vty.h"
31#include "plist.h"
0f588989
DS
32#include "hash.h"
33#include "jhash.h"
9c5e4d62 34#include "wheel.h"
12e41d03
DL
35
36#include "pimd.h"
37#include "pim_pim.h"
38#include "pim_str.h"
39#include "pim_time.h"
40#include "pim_iface.h"
41#include "pim_join.h"
42#include "pim_zlookup.h"
43#include "pim_upstream.h"
44#include "pim_ifchannel.h"
45#include "pim_neighbor.h"
46#include "pim_rpf.h"
47#include "pim_zebra.h"
48#include "pim_oil.h"
49#include "pim_macro.h"
8f5f5e91 50#include "pim_rp.h"
f14248dd 51#include "pim_br.h"
627ed2a3 52#include "pim_register.h"
3c72d654 53#include "pim_msdp.h"
982bff89 54#include "pim_jp_agg.h"
1bc98276 55#include "pim_nht.h"
15a5dafe 56#include "pim_ssm.h"
12e41d03 57
0f588989
DS
58struct hash *pim_upstream_hash = NULL;
59struct list *pim_upstream_list = NULL;
9c5e4d62 60struct timer_wheel *pim_upstream_sg_wheel = NULL;
0f588989 61
982bff89 62static void join_timer_stop(struct pim_upstream *up);
12e41d03
DL
63static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up);
64
cfa91a87
DS
65/*
66 * A (*,G) or a (*,*) is going away
67 * remove the parent pointer from
68 * those pointing at us
69 */
70static void
71pim_upstream_remove_children (struct pim_upstream *up)
72{
cfa91a87
DS
73 struct pim_upstream *child;
74
03417ccd 75 if (!up->sources)
cfa91a87
DS
76 return;
77
03417ccd 78 while (!list_isempty (up->sources))
cfa91a87 79 {
03417ccd
DS
80 child = listnode_head (up->sources);
81 child->parent = NULL;
82 listnode_delete (up->sources, child);
cfa91a87
DS
83 }
84}
85
86/*
87 * A (*,G) or a (*,*) is being created
88 * Find the children that would point
89 * at us.
90 */
91static void
92pim_upstream_find_new_children (struct pim_upstream *up)
93{
94 struct pim_upstream *child;
95 struct listnode *ch_node;
96
4ed0af70
DS
97 if ((up->sg.src.s_addr != INADDR_ANY) &&
98 (up->sg.grp.s_addr != INADDR_ANY))
cfa91a87
DS
99 return;
100
4ed0af70
DS
101 if ((up->sg.src.s_addr == INADDR_ANY) &&
102 (up->sg.grp.s_addr == INADDR_ANY))
cfa91a87
DS
103 return;
104
0f588989 105 for (ALL_LIST_ELEMENTS_RO (pim_upstream_list, ch_node, child))
cfa91a87 106 {
4ed0af70 107 if ((up->sg.grp.s_addr != INADDR_ANY) &&
0f588989 108 (child->sg.grp.s_addr == up->sg.grp.s_addr) &&
cfa91a87 109 (child != up))
03417ccd
DS
110 {
111 child->parent = up;
112 listnode_add_sort (up->sources, child);
113 }
cfa91a87
DS
114 }
115}
116
4d99418b
DS
117/*
118 * If we have a (*,*) || (S,*) there is no parent
119 * If we have a (S,G), find the (*,G)
120 * If we have a (*,G), find the (*,*)
121 */
122static struct pim_upstream *
03417ccd 123pim_upstream_find_parent (struct pim_upstream *child)
4d99418b 124{
03417ccd
DS
125 struct prefix_sg any = child->sg;
126 struct pim_upstream *up = NULL;
4d99418b
DS
127
128 // (S,G)
03417ccd
DS
129 if ((child->sg.src.s_addr != INADDR_ANY) &&
130 (child->sg.grp.s_addr != INADDR_ANY))
4d99418b 131 {
4ed0af70 132 any.src.s_addr = INADDR_ANY;
03417ccd
DS
133 up = pim_upstream_find (&any);
134
135 if (up)
136 listnode_add (up->sources, child);
137
138 return up;
4d99418b
DS
139 }
140
03417ccd 141 return NULL;
4d99418b
DS
142}
143
12e41d03
DL
144void pim_upstream_free(struct pim_upstream *up)
145{
146 XFREE(MTYPE_PIM_UPSTREAM, up);
1bc98276 147 up = NULL;
12e41d03
DL
148}
149
150static void upstream_channel_oil_detach(struct pim_upstream *up)
151{
152 if (up->channel_oil) {
153 pim_channel_oil_del(up->channel_oil);
25a335e0 154 up->channel_oil = NULL;
12e41d03
DL
155 }
156}
157
d0db90bf 158struct pim_upstream *
e5905a3b 159pim_upstream_del(struct pim_upstream *up, const char *name)
12e41d03 160{
7667c556 161 bool notify_msdp = false;
1bc98276 162 struct prefix nht_p;
7667c556 163
3197cb1e 164 if (PIM_DEBUG_TRACE)
815c33c9
CS
165 zlog_debug ("%s(%s): Delete %s ref count: %d , flags: %d c_oil ref count %d (Pre decrement)",
166 __PRETTY_FUNCTION__, name, up->sg_str, up->ref_count, up->flags,
167 up->channel_oil->oil_ref_count);
3197cb1e 168
e5905a3b
DS
169 --up->ref_count;
170
171 if (up->ref_count >= 1)
d0db90bf 172 return up;
e5905a3b 173
f14248dd 174 THREAD_OFF(up->t_ka_timer);
792f4d29 175 THREAD_OFF(up->t_rs_timer);
1bf16443 176 THREAD_OFF(up->t_msdp_reg_timer);
12e41d03 177
7667c556 178 if (up->join_state == PIM_UPSTREAM_JOINED) {
982bff89
DS
179 pim_jp_agg_single_upstream_send (&up->rpf, up, 0);
180
7667c556 181 if (up->sg.src.s_addr == INADDR_ANY) {
182 /* if a (*, G) entry in the joined state is being deleted we
183 * need to notify MSDP */
184 notify_msdp = true;
185 }
186 }
187
fc9d070d 188 join_timer_stop(up);
06e12762 189 pim_jp_agg_upstream_verification (up, false);
fc9d070d
DS
190 up->rpf.source_nexthop.interface = NULL;
191
36e466fe 192 if (up->sg.src.s_addr != INADDR_ANY) {
9c5e4d62 193 wheel_remove_item (pim_upstream_sg_wheel, up);
36e466fe 194 notify_msdp = true;
195 }
9c5e4d62 196
cfa91a87 197 pim_upstream_remove_children (up);
6a78764e 198 pim_mroute_del (up->channel_oil, __PRETTY_FUNCTION__);
12e41d03
DL
199 upstream_channel_oil_detach(up);
200
03417ccd 201 if (up->sources)
850a9f99
DS
202 {
203 struct listnode *node, *nnode;
204 struct pim_upstream *child;
205 for (ALL_LIST_ELEMENTS (up->sources, node, nnode, child))
206 {
207 if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(child->flags))
208 {
209 PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(child->flags);
210 pim_upstream_del(child, __PRETTY_FUNCTION__);
211 }
212 }
213
214 list_delete (up->sources);
215 }
03417ccd
DS
216 up->sources = NULL;
217
2a28f7a0 218 list_delete (up->ifchannels);
4ba87bb9 219 up->ifchannels = NULL;
2a28f7a0 220
12e41d03
DL
221 /*
222 notice that listnode_delete() can't be moved
223 into pim_upstream_free() because the later is
224 called by list_delete_all_node()
225 */
03417ccd
DS
226 if (up->parent)
227 {
228 listnode_delete (up->parent->sources, up);
229 up->parent = NULL;
230 }
0f588989
DS
231 listnode_delete (pim_upstream_list, up);
232 hash_release (pim_upstream_hash, up);
12e41d03 233
1bc98276
CS
234 if (notify_msdp)
235 {
236 pim_msdp_up_del (&up->sg);
237 }
238
239 /* Deregister addr with Zebra NHT */
240 nht_p.family = AF_INET;
241 nht_p.prefixlen = IPV4_MAX_BITLEN;
242 nht_p.u.prefix4 = up->upstream_addr;
243 if (PIM_DEBUG_TRACE)
244 {
245 char buf[PREFIX2STR_BUFFER];
246 prefix2str (&nht_p, buf, sizeof (buf));
4ba87bb9 247 zlog_debug ("%s: Deregister upstream %s addr %s with Zebra NHT",
633988a7 248 __PRETTY_FUNCTION__, up->sg_str, buf);
1bc98276
CS
249 }
250 pim_delete_tracked_nexthop (&nht_p, up, NULL);
251
252 pim_upstream_free (up);
d0db90bf
DS
253
254 return NULL;
12e41d03
DL
255}
256
56638739
DS
257void
258pim_upstream_send_join (struct pim_upstream *up)
12e41d03 259{
3197cb1e 260 if (PIM_DEBUG_TRACE) {
eaa54bdb 261 char rpf_str[PREFIX_STRLEN];
63c59d0c 262 pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
05e451f8 263 zlog_debug ("%s: RPF'%s=%s(%s) for Interface %s", __PRETTY_FUNCTION__,
8bfb8b67 264 up->sg_str, rpf_str, pim_upstream_state2str (up->join_state),
56638739 265 up->rpf.source_nexthop.interface->name);
63c59d0c 266 if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
05e451f8 267 zlog_debug("%s: can't send join upstream: RPF'%s=%s",
56638739 268 __PRETTY_FUNCTION__,
8bfb8b67 269 up->sg_str, rpf_str);
12e41d03
DL
270 /* warning only */
271 }
272 }
56638739 273
12e41d03 274 /* send Join(S,G) to the current upstream neighbor */
982bff89 275 pim_jp_agg_single_upstream_send(&up->rpf, up, 1 /* join */);
12e41d03
DL
276}
277
278static int on_join_timer(struct thread *t)
279{
280 struct pim_upstream *up;
281
12e41d03 282 up = THREAD_ARG(t);
12e41d03 283
bb6e291f
DS
284 up->t_join_timer = NULL;
285
286 /*
287 * In the case of a HFR we will not ahve anyone to send this to.
288 */
0bf27c5c 289 if (PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
bb6e291f
DS
290 return 0;
291
eb345962
DS
292 /*
293 * Don't send the join if the outgoing interface is a loopback
294 * But since this might change leave the join timer running
295 */
fc9d070d
DS
296 if (up->rpf.source_nexthop.interface &&
297 !if_is_loopback (up->rpf.source_nexthop.interface))
eb345962 298 pim_upstream_send_join (up);
12e41d03 299
12e41d03
DL
300 join_timer_start(up);
301
302 return 0;
303}
304
982bff89 305static void join_timer_stop(struct pim_upstream *up)
12e41d03 306{
982bff89
DS
307 struct pim_neighbor *nbr;
308
7eb90689
DS
309 THREAD_OFF (up->t_join_timer);
310
982bff89
DS
311 nbr = pim_neighbor_find (up->rpf.source_nexthop.interface,
312 up->rpf.rpf_addr.u.prefix4);
313
314 if (nbr)
315 pim_jp_agg_remove_group (nbr->upstream_jp_agg, up);
316
06e12762 317 pim_jp_agg_upstream_verification (up, false);
982bff89
DS
318}
319
320void
321join_timer_start(struct pim_upstream *up)
322{
fc9d070d 323 struct pim_neighbor *nbr = NULL;
982bff89 324
fc9d070d
DS
325 if (up->rpf.source_nexthop.interface)
326 {
327 nbr = pim_neighbor_find (up->rpf.source_nexthop.interface,
328 up->rpf.rpf_addr.u.prefix4);
329
330 if (PIM_DEBUG_PIM_EVENTS) {
331 zlog_debug("%s: starting %d sec timer for upstream (S,G)=%s",
332 __PRETTY_FUNCTION__,
333 qpim_t_periodic,
334 up->sg_str);
335 }
336 }
12e41d03 337
982bff89
DS
338 if (nbr)
339 pim_jp_agg_add_group (nbr->upstream_jp_agg, up, 1);
340 else
341 {
342 THREAD_OFF (up->t_join_timer);
343 THREAD_TIMER_ON(master, up->t_join_timer,
344 on_join_timer,
345 up, qpim_t_periodic);
346 }
06e12762 347 pim_jp_agg_upstream_verification (up, true);
12e41d03
DL
348}
349
982bff89
DS
350/*
351 * This is only called when we are switching the upstream
352 * J/P from one neighbor to another
353 *
354 * As such we need to remove from the old list and
355 * add to the new list.
356 */
357void pim_upstream_join_timer_restart(struct pim_upstream *up, struct pim_rpf *old)
12e41d03 358{
982bff89 359 //THREAD_OFF(up->t_join_timer);
12e41d03
DL
360 join_timer_start(up);
361}
362
363static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up,
364 int interval_msec)
365{
366 if (PIM_DEBUG_PIM_EVENTS) {
05e451f8 367 zlog_debug("%s: restarting %d msec timer for upstream (S,G)=%s",
12e41d03
DL
368 __PRETTY_FUNCTION__,
369 interval_msec,
8bfb8b67 370 up->sg_str);
12e41d03
DL
371 }
372
373 THREAD_OFF(up->t_join_timer);
374 THREAD_TIMER_MSEC_ON(master, up->t_join_timer,
375 on_join_timer,
376 up, interval_msec);
377}
378
379void pim_upstream_join_suppress(struct pim_upstream *up,
380 struct in_addr rpf_addr,
381 int holdtime)
382{
383 long t_joinsuppress_msec;
384 long join_timer_remain_msec;
385
386 t_joinsuppress_msec = MIN(pim_if_t_suppressed_msec(up->rpf.source_nexthop.interface),
387 1000 * holdtime);
388
389 join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
390
3197cb1e 391 if (PIM_DEBUG_TRACE) {
eaa54bdb 392 char rpf_str[INET_ADDRSTRLEN];
12e41d03 393 pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
05e451f8 394 zlog_debug("%s %s: detected Join%s to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
12e41d03 395 __FILE__, __PRETTY_FUNCTION__,
8bfb8b67 396 up->sg_str,
12e41d03
DL
397 rpf_str,
398 join_timer_remain_msec, t_joinsuppress_msec);
399 }
400
401 if (join_timer_remain_msec < t_joinsuppress_msec) {
3197cb1e 402 if (PIM_DEBUG_TRACE) {
05e451f8 403 zlog_debug("%s %s: suppressing Join(S,G)=%s for %ld msec",
12e41d03 404 __FILE__, __PRETTY_FUNCTION__,
8bfb8b67 405 up->sg_str, t_joinsuppress_msec);
12e41d03
DL
406 }
407
408 pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec);
409 }
410}
411
412void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
c48a612c 413 struct pim_upstream *up)
12e41d03
DL
414{
415 long join_timer_remain_msec;
416 int t_override_msec;
417
418 join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
419 t_override_msec = pim_if_t_override_msec(up->rpf.source_nexthop.interface);
420
3197cb1e 421 if (PIM_DEBUG_TRACE) {
eaa54bdb 422 char rpf_str[INET_ADDRSTRLEN];
c48a612c 423 pim_inet4_dump("<rpf?>", up->rpf.rpf_addr.u.prefix4, rpf_str, sizeof(rpf_str));
05e451f8 424 zlog_debug("%s: to RPF'%s=%s: join_timer=%ld msec t_override=%d msec",
12e41d03 425 debug_label,
8bfb8b67 426 up->sg_str, rpf_str,
12e41d03
DL
427 join_timer_remain_msec, t_override_msec);
428 }
429
430 if (join_timer_remain_msec > t_override_msec) {
3197cb1e 431 if (PIM_DEBUG_TRACE) {
05e451f8 432 zlog_debug("%s: decreasing (S,G)=%s join timer to t_override=%d msec",
12e41d03 433 debug_label,
8bfb8b67 434 up->sg_str,
12e41d03
DL
435 t_override_msec);
436 }
437
438 pim_upstream_join_timer_restart_msec(up, t_override_msec);
439 }
440}
441
442static void forward_on(struct pim_upstream *up)
443{
12e41d03
DL
444 struct listnode *chnode;
445 struct listnode *chnextnode;
b5e6281b 446 struct pim_ifchannel *ch = NULL;
12e41d03 447
ea4a71fc 448 /* scan (S,G) state */
b5e6281b 449 for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) {
ea4a71fc
DS
450 if (pim_macro_chisin_oiflist(ch))
451 pim_forward_start(ch);
12e41d03 452
ea4a71fc 453 } /* scan iface channel list */
12e41d03
DL
454}
455
456static void forward_off(struct pim_upstream *up)
457{
12e41d03
DL
458 struct listnode *chnode;
459 struct listnode *chnextnode;
12e41d03
DL
460 struct pim_ifchannel *ch;
461
ea4a71fc 462 /* scan per-interface (S,G) state */
b5e6281b 463 for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) {
12e41d03 464
ea4a71fc 465 pim_forward_stop(ch);
12e41d03 466
ea4a71fc 467 } /* scan iface channel list */
12e41d03
DL
468}
469
bb6e291f
DS
470static int
471pim_upstream_could_register (struct pim_upstream *up)
472{
1131c2eb
CS
473 struct pim_interface *pim_ifp = NULL;
474
475 if (up->rpf.source_nexthop.interface)
476 pim_ifp = up->rpf.source_nexthop.interface->info;
477 else
478 {
479 if (PIM_DEBUG_TRACE)
480 zlog_debug ("%s: up %s RPF is not present", __PRETTY_FUNCTION__, up->sg_str);
481 }
bb6e291f 482
35917570 483 if (pim_ifp && PIM_I_am_DR (pim_ifp) &&
bb6e291f
DS
484 pim_if_connected_to_source (up->rpf.source_nexthop.interface, up->sg.src))
485 return 1;
486
487 return 0;
488}
489
15a5dafe 490/* Source registration is supressed for SSM groups. When the SSM range changes
491 * we re-revaluate register setup for existing upstream entries */
492void
493pim_upstream_register_reevaluate (void)
494{
495 struct listnode *upnode;
496 struct pim_upstream *up;
497
498 for (ALL_LIST_ELEMENTS_RO (pim_upstream_list, upnode, up))
499 {
500 /* If FHR is set CouldRegister is True. Also check if the flow
501 * is actually active; if it is not kat setup will trigger source
502 * registration whenever the flow becomes active. */
503 if (!PIM_UPSTREAM_FLAG_TEST_FHR (up->flags) || !up->t_ka_timer)
504 continue;
505
506 if (pim_is_grp_ssm (up->sg.grp))
507 {
508 /* clear the register state for SSM groups */
509 if (up->reg_state != PIM_REG_NOINFO)
510 {
511 if (PIM_DEBUG_PIM_EVENTS)
512 zlog_debug ("Clear register for %s as G is now SSM",
513 up->sg_str);
514 /* remove regiface from the OIL if it is there*/
515 pim_channel_del_oif (up->channel_oil, pim_regiface,
516 PIM_OIF_FLAG_PROTO_PIM);
517 up->reg_state = PIM_REG_NOINFO;
518 }
519 }
520 else
521 {
522 /* register ASM sources with the RP */
523 if (up->reg_state == PIM_REG_NOINFO)
524 {
525 if (PIM_DEBUG_PIM_EVENTS)
526 zlog_debug ("Register %s as G is now ASM", up->sg_str);
527 pim_channel_add_oif (up->channel_oil, pim_regiface,
528 PIM_OIF_FLAG_PROTO_PIM);
529 up->reg_state = PIM_REG_JOIN;
530 }
531 }
532 }
533}
534
7fcdfb34
DS
535void
536pim_upstream_switch(struct pim_upstream *up,
537 enum pim_upstream_state new_state)
12e41d03
DL
538{
539 enum pim_upstream_state old_state = up->join_state;
540
12e41d03 541 if (PIM_DEBUG_PIM_EVENTS) {
c9802954 542 zlog_debug("%s: PIM_UPSTREAM_%s: (S,G) old: %s new: %s",
12e41d03 543 __PRETTY_FUNCTION__,
8bfb8b67 544 up->sg_str,
c9802954
DS
545 pim_upstream_state2str (up->join_state),
546 pim_upstream_state2str (new_state));
12e41d03
DL
547 }
548
b4786acd 549 up->join_state = new_state;
550 if (old_state != new_state)
551 up->state_transition = pim_time_monotonic_sec();
bb027ee8 552
12e41d03
DL
553 pim_upstream_update_assert_tracking_desired(up);
554
555 if (new_state == PIM_UPSTREAM_JOINED) {
81900c5a
DS
556 if (old_state != PIM_UPSTREAM_JOINED)
557 {
0bf27c5c 558 int old_fhr = PIM_UPSTREAM_FLAG_TEST_FHR(up->flags);
81900c5a 559 forward_on(up);
7667c556 560 pim_msdp_up_join_state_changed(up);
0bf27c5c 561 if (pim_upstream_could_register (up))
bb6e291f 562 {
0bf27c5c 563 PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
a9b59879 564 if (!old_fhr && PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags))
bb6e291f
DS
565 {
566 pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
15a5dafe 567 pim_register_join (up);
bb6e291f
DS
568 }
569 }
570 else
571 {
572 pim_upstream_send_join (up);
573 join_timer_start (up);
574 }
81900c5a
DS
575 }
576 else
577 {
578 forward_on (up);
579 }
12e41d03
DL
580 }
581 else {
982bff89 582
12e41d03 583 forward_off(up);
7667c556 584 if (old_state == PIM_UPSTREAM_JOINED)
585 pim_msdp_up_join_state_changed(up);
982bff89
DS
586
587 pim_jp_agg_single_upstream_send(&up->rpf, up, 0 /* prune */);
588 join_timer_stop(up);
12e41d03 589 }
12e41d03
DL
590}
591
1bc98276 592int
03417ccd
DS
593pim_upstream_compare (void *arg1, void *arg2)
594{
595 const struct pim_upstream *up1 = (const struct pim_upstream *)arg1;
596 const struct pim_upstream *up2 = (const struct pim_upstream *)arg2;
597
598 if (ntohl(up1->sg.grp.s_addr) < ntohl(up2->sg.grp.s_addr))
599 return -1;
600
601 if (ntohl(up1->sg.grp.s_addr) > ntohl(up2->sg.grp.s_addr))
602 return 1;
603
604 if (ntohl(up1->sg.src.s_addr) < ntohl(up2->sg.src.s_addr))
605 return -1;
606
607 if (ntohl(up1->sg.src.s_addr) > ntohl(up2->sg.src.s_addr))
608 return 1;
609
610 return 0;
611}
612
07335c8d
DS
613static struct pim_upstream *
614pim_upstream_new (struct prefix_sg *sg,
615 struct interface *incoming,
616 int flags)
12e41d03 617{
2f702571 618 enum pim_rpf_result rpf_result;
07335c8d
DS
619 struct pim_interface *pim_ifp;
620 struct pim_upstream *up;
12e41d03 621
36d9e7dc 622 up = XCALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
1bc98276
CS
623 if (!up)
624 {
625 zlog_err("%s: PIM XCALLOC(%zu) failure",
12e41d03 626 __PRETTY_FUNCTION__, sizeof(*up));
1bc98276
CS
627 return NULL;
628 }
12e41d03 629
5074a423 630 up->sg = *sg;
8bfb8b67 631 pim_str_sg_set (sg, up->sg_str);
0f588989 632 up = hash_get (pim_upstream_hash, up, hash_alloc_intern);
36d6bd7d 633 if (!pim_rp_set_upstream_addr (&up->upstream_addr, sg->src, sg->grp))
8f5f5e91 634 {
3197cb1e 635 if (PIM_DEBUG_TRACE)
8f5f5e91
DS
636 zlog_debug("%s: Received a (*,G) with no RP configured", __PRETTY_FUNCTION__);
637
03417ccd 638 hash_release (pim_upstream_hash, up);
8f5f5e91
DS
639 XFREE (MTYPE_PIM_UPSTREAM, up);
640 return NULL;
641 }
642
03417ccd
DS
643 up->parent = pim_upstream_find_parent (up);
644 if (up->sg.src.s_addr == INADDR_ANY)
645 {
646 up->sources = list_new ();
647 up->sources->cmp = pim_upstream_compare;
648 }
649 else
650 up->sources = NULL;
651
cfa91a87 652 pim_upstream_find_new_children (up);
4a40c37a 653 up->flags = flags;
12e41d03 654 up->ref_count = 1;
4a4c4a07
DS
655 up->t_join_timer = NULL;
656 up->t_ka_timer = NULL;
792f4d29 657 up->t_rs_timer = NULL;
1bf16443 658 up->t_msdp_reg_timer = NULL;
b4786acd 659 up->join_state = PIM_UPSTREAM_NOTJOINED;
0c2ebf00 660 up->reg_state = PIM_REG_NOINFO;
12e41d03 661 up->state_transition = pim_time_monotonic_sec();
4a4c4a07 662 up->channel_oil = NULL;
f9e0ab5b 663 up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE;
12e41d03 664
f24405b1 665 up->rpf.source_nexthop.interface = NULL;
63c59d0c
DS
666 up->rpf.source_nexthop.mrib_nexthop_addr.family = AF_INET;
667 up->rpf.source_nexthop.mrib_nexthop_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY;
12e41d03
DL
668 up->rpf.source_nexthop.mrib_metric_preference = qpim_infinite_assert_metric.metric_preference;
669 up->rpf.source_nexthop.mrib_route_metric = qpim_infinite_assert_metric.route_metric;
63c59d0c
DS
670 up->rpf.rpf_addr.family = AF_INET;
671 up->rpf.rpf_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY;
12e41d03 672
2a28f7a0
DS
673 up->ifchannels = list_new();
674 up->ifchannels->cmp = (int (*)(void *, void *))pim_ifchannel_compare;
675
9c5e4d62
DS
676 if (up->sg.src.s_addr != INADDR_ANY)
677 wheel_add_item (pim_upstream_sg_wheel, up);
678
1bc98276 679 rpf_result = pim_rpf_update(up, NULL, 1);
2f702571 680 if (rpf_result == PIM_RPF_FAILURE) {
ec97ac65
DS
681 struct prefix nht_p;
682
3197cb1e 683 if (PIM_DEBUG_TRACE)
4a40c37a 684 zlog_debug ("%s: Attempting to create upstream(%s), Unable to RPF for source", __PRETTY_FUNCTION__,
8bfb8b67 685 up->sg_str);
03417ccd 686
ec97ac65
DS
687 nht_p.family = AF_INET;
688 nht_p.prefixlen = IPV4_MAX_BITLEN;
689 nht_p.u.prefix4 = up->upstream_addr;
690 pim_delete_tracked_nexthop (&nht_p, up, NULL);
691
03417ccd
DS
692 if (up->parent)
693 {
694 listnode_delete (up->parent->sources, up);
695 up->parent = NULL;
696 }
9c5e4d62
DS
697
698 if (up->sg.src.s_addr != INADDR_ANY)
699 wheel_remove_item (pim_upstream_sg_wheel, up);
700
03417ccd
DS
701 pim_upstream_remove_children (up);
702 if (up->sources)
703 list_delete (up->sources);
704
705 hash_release (pim_upstream_hash, up);
2f702571
DS
706 XFREE(MTYPE_PIM_UPSTREAM, up);
707 return NULL;
708 }
12e41d03 709
633988a7
CS
710 if (up->rpf.source_nexthop.interface)
711 {
712 pim_ifp = up->rpf.source_nexthop.interface->info;
713 if (pim_ifp)
714 up->channel_oil = pim_channel_oil_add(&up->sg, pim_ifp->mroute_vif_index);
715 }
0f588989 716 listnode_add_sort(pim_upstream_list, up);
12e41d03 717
3197cb1e 718 if (PIM_DEBUG_TRACE)
1bc98276
CS
719 {
720 zlog_debug ("%s: Created Upstream %s upstream_addr %s",
721 __PRETTY_FUNCTION__, up->sg_str,
722 inet_ntoa (up->upstream_addr));
723 }
03417ccd 724
12e41d03
DL
725 return up;
726}
727
4ed0af70 728struct pim_upstream *pim_upstream_find(struct prefix_sg *sg)
12e41d03 729{
0f588989
DS
730 struct pim_upstream lookup;
731 struct pim_upstream *up = NULL;
12e41d03 732
0f588989
DS
733 lookup.sg = *sg;
734 up = hash_lookup (pim_upstream_hash, &lookup);
735 return up;
12e41d03
DL
736}
737
e711cd3c
DS
738struct pim_upstream *
739pim_upstream_find_or_add(struct prefix_sg *sg,
740 struct interface *incoming,
741 int flags, const char *name)
742{
743 struct pim_upstream *up;
744
745 up = pim_upstream_find(sg);
746
747 if (up)
748 {
749 if (!(up->flags & flags))
750 {
751 up->flags |= flags;
752 up->ref_count++;
753 }
754 }
755 else
756 up = pim_upstream_add (sg, incoming, flags, name);
757
758 return up;
759}
760
d0db90bf
DS
761void
762pim_upstream_ref(struct pim_upstream *up, int flags)
1bf16443 763{
764 up->flags |= flags;
765 ++up->ref_count;
766}
767
4ed0af70 768struct pim_upstream *pim_upstream_add(struct prefix_sg *sg,
4a40c37a 769 struct interface *incoming,
e5905a3b 770 int flags, const char *name)
12e41d03 771{
594a78cc 772 struct pim_upstream *up = NULL;
e5905a3b 773 int found = 0;
5074a423 774 up = pim_upstream_find(sg);
12e41d03 775 if (up) {
1bf16443 776 pim_upstream_ref(up, flags);
e5905a3b 777 found = 1;
12e41d03
DL
778 }
779 else {
4a40c37a 780 up = pim_upstream_new(sg, incoming, flags);
12e41d03
DL
781 }
782
e5905a3b 783 if (PIM_DEBUG_TRACE)
f4075cb4
DS
784 {
785 if (up)
633988a7
CS
786 {
787 char buf[PREFIX2STR_BUFFER];
788 prefix2str (&up->rpf.rpf_addr, buf, sizeof (buf));
789 zlog_debug("%s(%s): %s, iif %s found: %d: ref_count: %d",
f4075cb4 790 __PRETTY_FUNCTION__, name,
633988a7 791 up->sg_str, buf, found,
f4075cb4 792 up->ref_count);
633988a7 793 }
f4075cb4
DS
794 else
795 zlog_debug("%s(%s): (%s) failure to create",
796 __PRETTY_FUNCTION__, name,
797 pim_str_sg_dump (sg));
798 }
12e41d03 799
e5905a3b 800 return up;
12e41d03
DL
801}
802
c8fc07cb
DS
803/*
804 * Passed in up must be the upstream for ch. starch is NULL if no
805 * information
806 */
9f44d042 807int
7a3ddda5 808pim_upstream_evaluate_join_desired_interface (struct pim_upstream *up,
c8fc07cb
DS
809 struct pim_ifchannel *ch,
810 struct pim_ifchannel *starch)
7a3ddda5 811{
c8fc07cb 812 if (ch)
7a3ddda5 813 {
46c8aab8 814 if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags))
c8fc07cb 815 return 0;
d1e77284
DS
816
817 if (!pim_macro_ch_lost_assert(ch) && pim_macro_chisin_joins_or_include(ch))
c8fc07cb 818 return 1;
7a3ddda5 819 }
46c8aab8 820
7a3ddda5
DS
821 /*
822 * joins (*,G)
823 */
c8fc07cb 824 if (starch)
7a3ddda5 825 {
c8fc07cb
DS
826 if (PIM_IF_FLAG_TEST_S_G_RPT (starch->upstream->flags))
827 return 0;
828
829 if (!pim_macro_ch_lost_assert (starch) && pim_macro_chisin_joins_or_include (starch))
830 return 1;
7a3ddda5
DS
831 }
832
833 return 0;
834}
835
12e41d03
DL
836/*
837 Evaluate JoinDesired(S,G):
838
839 JoinDesired(S,G) is true if there is a downstream (S,G) interface I
840 in the set:
841
842 inherited_olist(S,G) =
843 joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
844
845 JoinDesired(S,G) may be affected by changes in the following:
846
847 pim_ifp->primary_address
848 pim_ifp->pim_dr_addr
849 ch->ifassert_winner_metric
850 ch->ifassert_winner
851 ch->local_ifmembership
852 ch->ifjoin_state
853 ch->upstream->rpf.source_nexthop.mrib_metric_preference
854 ch->upstream->rpf.source_nexthop.mrib_route_metric
855 ch->upstream->rpf.source_nexthop.interface
856
857 See also pim_upstream_update_join_desired() below.
858 */
859int pim_upstream_evaluate_join_desired(struct pim_upstream *up)
860{
c8fc07cb
DS
861 struct interface *ifp;
862 struct listnode *node;
863 struct pim_ifchannel *ch, *starch;
864 struct pim_upstream *starup = up->parent;
7a3ddda5 865 int ret = 0;
12e41d03 866
c8fc07cb 867 for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp))
ea4a71fc 868 {
c8fc07cb
DS
869 if (!ifp->info)
870 continue;
871
872 ch = pim_ifchannel_find (ifp, &up->sg);
873
874 if (starup)
875 starch = pim_ifchannel_find (ifp, &starup->sg);
876 else
877 starch = NULL;
878
879 if (!ch && !starch)
880 continue;
12e41d03 881
c8fc07cb 882 ret += pim_upstream_evaluate_join_desired_interface (up, ch, starch);
ea4a71fc 883 } /* scan iface channel list */
12e41d03 884
7a3ddda5 885 return ret; /* false */
12e41d03
DL
886}
887
888/*
889 See also pim_upstream_evaluate_join_desired() above.
890*/
891void pim_upstream_update_join_desired(struct pim_upstream *up)
892{
893 int was_join_desired; /* boolean */
894 int is_join_desired; /* boolean */
895
896 was_join_desired = PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags);
897
898 is_join_desired = pim_upstream_evaluate_join_desired(up);
899 if (is_join_desired)
900 PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up->flags);
901 else
902 PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up->flags);
903
904 /* switched from false to true */
905 if (is_join_desired && !was_join_desired) {
12e41d03
DL
906 pim_upstream_switch(up, PIM_UPSTREAM_JOINED);
907 return;
908 }
909
910 /* switched from true to false */
911 if (!is_join_desired && was_join_desired) {
12e41d03
DL
912 pim_upstream_switch(up, PIM_UPSTREAM_NOTJOINED);
913 return;
914 }
915}
916
917/*
918 RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
919 Transitions from Joined State
920 RPF'(S,G) GenID changes
921
922 The upstream (S,G) state machine remains in Joined state. If the
923 Join Timer is set to expire in more than t_override seconds, reset
924 it so that it expires after t_override seconds.
925*/
926void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr)
927{
928 struct listnode *up_node;
929 struct listnode *up_nextnode;
930 struct pim_upstream *up;
931
932 /*
0f588989
DS
933 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
934 */
935 for (ALL_LIST_ELEMENTS(pim_upstream_list, up_node, up_nextnode, up)) {
12e41d03 936
3197cb1e 937 if (PIM_DEBUG_TRACE) {
eaa54bdb
DW
938 char neigh_str[INET_ADDRSTRLEN];
939 char rpf_addr_str[PREFIX_STRLEN];
12e41d03 940 pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
63c59d0c 941 pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
05e451f8 942 zlog_debug("%s: matching neigh=%s against upstream (S,G)=%s joined=%d rpf_addr=%s",
12e41d03 943 __PRETTY_FUNCTION__,
8bfb8b67 944 neigh_str, up->sg_str,
12e41d03
DL
945 up->join_state == PIM_UPSTREAM_JOINED,
946 rpf_addr_str);
947 }
948
949 /* consider only (S,G) upstream in Joined state */
950 if (up->join_state != PIM_UPSTREAM_JOINED)
951 continue;
952
953 /* match RPF'(S,G)=neigh_addr */
63c59d0c 954 if (up->rpf.rpf_addr.u.prefix4.s_addr != neigh_addr.s_addr)
12e41d03
DL
955 continue;
956
957 pim_upstream_join_timer_decrease_to_t_override("RPF'(S,G) GenID change",
c48a612c 958 up);
12e41d03
DL
959 }
960}
961
962
963void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
964 struct interface *old_rpf_ifp)
965{
ea4a71fc
DS
966 struct listnode *chnode;
967 struct listnode *chnextnode;
968 struct pim_ifchannel *ch;
ea4a71fc
DS
969
970 /* search all ifchannels */
b5e6281b 971 for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) {
ea4a71fc
DS
972 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
973 if (
974 /* RPF_interface(S) was NOT I */
975 (old_rpf_ifp == ch->interface)
976 &&
977 /* RPF_interface(S) stopped being I */
978 (ch->upstream->rpf.source_nexthop.interface != ch->interface)
979 ) {
980 assert_action_a5(ch);
981 }
982 } /* PIM_IFASSERT_I_AM_LOSER */
12e41d03 983
ea4a71fc 984 pim_ifchannel_update_assert_tracking_desired(ch);
12e41d03
DL
985 }
986}
987
988void pim_upstream_update_could_assert(struct pim_upstream *up)
989{
12e41d03
DL
990 struct listnode *chnode;
991 struct listnode *chnextnode;
12e41d03
DL
992 struct pim_ifchannel *ch;
993
ea4a71fc 994 /* scan per-interface (S,G) state */
b5e6281b 995 for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) {
ea4a71fc
DS
996 pim_ifchannel_update_could_assert(ch);
997 } /* scan iface channel list */
12e41d03
DL
998}
999
1000void pim_upstream_update_my_assert_metric(struct pim_upstream *up)
1001{
12e41d03
DL
1002 struct listnode *chnode;
1003 struct listnode *chnextnode;
12e41d03
DL
1004 struct pim_ifchannel *ch;
1005
ea4a71fc 1006 /* scan per-interface (S,G) state */
b5e6281b 1007 for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) {
ea4a71fc 1008 pim_ifchannel_update_my_assert_metric(ch);
12e41d03 1009
ea4a71fc 1010 } /* scan iface channel list */
12e41d03
DL
1011}
1012
1013static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up)
1014{
12e41d03
DL
1015 struct listnode *chnode;
1016 struct listnode *chnextnode;
12e41d03
DL
1017 struct pim_interface *pim_ifp;
1018 struct pim_ifchannel *ch;
1019
ea4a71fc 1020 /* scan per-interface (S,G) state */
4ba87bb9
CS
1021 for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch))
1022 {
1023 if (!ch->interface)
1024 continue;
1025 pim_ifp = ch->interface->info;
1026 if (!pim_ifp)
1027 continue;
12e41d03 1028
4ba87bb9 1029 pim_ifchannel_update_assert_tracking_desired(ch);
12e41d03 1030
4ba87bb9 1031 } /* scan iface channel list */
12e41d03 1032}
f14248dd 1033
1bf16443 1034/* When kat is stopped CouldRegister goes to false so we need to
1035 * transition the (S, G) on FHR to NI state and remove reg tunnel
1036 * from the OIL */
1037static void pim_upstream_fhr_kat_expiry(struct pim_upstream *up)
1038{
1039 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
1040 return;
1041
1042 if (PIM_DEBUG_TRACE)
8bfb8b67
DS
1043 zlog_debug ("kat expired on %s; clear fhr reg state", up->sg_str);
1044
1bf16443 1045 /* stop reg-stop timer */
1046 THREAD_OFF(up->t_rs_timer);
1047 /* remove regiface from the OIL if it is there*/
1048 pim_channel_del_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
0c2ebf00 1049 /* clear the register state */
1050 up->reg_state = PIM_REG_NOINFO;
1bf16443 1051 PIM_UPSTREAM_FLAG_UNSET_FHR(up->flags);
1052}
1053
1054/* When kat is started CouldRegister can go to true. And if it does we
1055 * need to transition the (S, G) on FHR to JOINED state and add reg tunnel
1056 * to the OIL */
1057static void pim_upstream_fhr_kat_start(struct pim_upstream *up)
1058{
1059 if (pim_upstream_could_register(up)) {
1060 if (PIM_DEBUG_TRACE)
8bfb8b67
DS
1061 zlog_debug ("kat started on %s; set fhr reg state to joined", up->sg_str);
1062
1bf16443 1063 PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
15a5dafe 1064 if (up->reg_state == PIM_REG_NOINFO)
1065 pim_register_join (up);
1bf16443 1066 }
1067}
1068
f14248dd
DS
1069/*
1070 * On an RP, the PMBR value must be cleared when the
1071 * Keepalive Timer expires
1bf16443 1072 * KAT expiry indicates that flow is inactive. If the flow was created or
1073 * maintained by activity now is the time to deref it.
f14248dd
DS
1074 */
1075static int
1076pim_upstream_keep_alive_timer (struct thread *t)
1077{
1078 struct pim_upstream *up;
1079
1080 up = THREAD_ARG(t);
1bf16443 1081 up->t_ka_timer = NULL;
f14248dd 1082
4ed0af70 1083 if (I_am_RP (up->sg.grp))
850a9f99
DS
1084 {
1085 pim_br_clear_pmbr (&up->sg);
1086 /*
1087 * We need to do more here :)
1088 * But this is the start.
1089 */
1090 }
14315ea8 1091
1bf16443 1092 /* source is no longer active - pull the SA from MSDP's cache */
1093 pim_msdp_sa_local_del(&up->sg);
14315ea8 1094
1bf16443 1095 /* if entry was created because of activity we need to deref it */
1096 if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags))
850a9f99
DS
1097 {
1098 pim_upstream_fhr_kat_expiry(up);
1099 if (PIM_DEBUG_TRACE)
1100 zlog_debug ("kat expired on %s; remove stream reference", up->sg_str);
1101 PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(up->flags);
1102 pim_upstream_del(up, __PRETTY_FUNCTION__);
1103 }
1104 else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up->flags))
1105 {
1106 PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(up->flags);
1107 pim_upstream_del(up, __PRETTY_FUNCTION__);
1108 }
14315ea8 1109
1bf16443 1110 return 0;
f14248dd
DS
1111}
1112
f14248dd
DS
1113void
1114pim_upstream_keep_alive_timer_start (struct pim_upstream *up,
1115 uint32_t time)
1116{
1bf16443 1117 if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) {
1118 if (PIM_DEBUG_TRACE)
8bfb8b67 1119 zlog_debug ("kat start on %s with no stream reference", up->sg_str);
1bf16443 1120 }
00d2f9e4 1121 THREAD_OFF (up->t_ka_timer);
f14248dd
DS
1122 THREAD_TIMER_ON (master,
1123 up->t_ka_timer,
1124 pim_upstream_keep_alive_timer,
1125 up, time);
1bf16443 1126
1127 /* any time keepalive is started against a SG we will have to
1128 * re-evaluate our active source database */
1129 pim_msdp_sa_local_update(up);
1130}
1131
1132/* MSDP on RP needs to know if a source is registerable to this RP */
1133static int
1134pim_upstream_msdp_reg_timer(struct thread *t)
1135{
1136 struct pim_upstream *up;
1137
1138 up = THREAD_ARG(t);
1139 up->t_msdp_reg_timer = NULL;
1140
1141 /* source is no longer active - pull the SA from MSDP's cache */
1142 pim_msdp_sa_local_del(&up->sg);
1143 return 1;
1144}
1145void
1146pim_upstream_msdp_reg_timer_start(struct pim_upstream *up)
1147{
1148 THREAD_OFF(up->t_msdp_reg_timer);
1149 THREAD_TIMER_ON(master, up->t_msdp_reg_timer,
1150 pim_upstream_msdp_reg_timer, up, PIM_MSDP_REG_RXED_PERIOD);
1151
1152 pim_msdp_sa_local_update(up);
f14248dd 1153}
cb40b272
DS
1154
1155/*
1156 * 4.2.1 Last-Hop Switchover to the SPT
1157 *
1158 * In Sparse-Mode PIM, last-hop routers join the shared tree towards the
1159 * RP. Once traffic from sources to joined groups arrives at a last-hop
1160 * router, it has the option of switching to receive the traffic on a
1161 * shortest path tree (SPT).
1162 *
1163 * The decision for a router to switch to the SPT is controlled as
1164 * follows:
1165 *
1166 * void
1167 * CheckSwitchToSpt(S,G) {
1168 * if ( ( pim_include(*,G) (-) pim_exclude(S,G)
1169 * (+) pim_include(S,G) != NULL )
1170 * AND SwitchToSptDesired(S,G) ) {
1171 * # Note: Restarting the KAT will result in the SPT switch
1172 * set KeepaliveTimer(S,G) to Keepalive_Period
1173 * }
1174 * }
1175 *
1176 * SwitchToSptDesired(S,G) is a policy function that is implementation
1177 * defined. An "infinite threshold" policy can be implemented by making
1178 * SwitchToSptDesired(S,G) return false all the time. A "switch on
1179 * first packet" policy can be implemented by making
1180 * SwitchToSptDesired(S,G) return true once a single packet has been
1181 * received for the source and group.
1182 */
1183int
4ed0af70 1184pim_upstream_switch_to_spt_desired (struct prefix_sg *sg)
cb40b272 1185{
4ed0af70 1186 if (I_am_RP (sg->grp))
a3b58b4a
DS
1187 return 1;
1188
cb40b272
DS
1189 return 0;
1190}
d7259eac 1191
80d9c3a0
DS
1192int
1193pim_upstream_is_sg_rpt (struct pim_upstream *up)
1194{
f21597f0
DS
1195 struct listnode *chnode;
1196 struct pim_ifchannel *ch;
1197
b5e6281b 1198 for (ALL_LIST_ELEMENTS_RO(up->ifchannels, chnode, ch))
f21597f0 1199 {
b5e6281b
DS
1200 if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags))
1201 return 1;
f21597f0 1202 }
e43b8697 1203
80d9c3a0
DS
1204 return 0;
1205}
3a66b17b
DS
1206/*
1207 * After receiving a packet set SPTbit:
1208 * void
1209 * Update_SPTbit(S,G,iif) {
1210 * if ( iif == RPF_interface(S)
1211 * AND JoinDesired(S,G) == TRUE
1212 * AND ( DirectlyConnected(S) == TRUE
1213 * OR RPF_interface(S) != RPF_interface(RP(G))
1214 * OR inherited_olist(S,G,rpt) == NULL
1215 * OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
1216 * ( RPF'(S,G) != NULL ) )
1217 * OR ( I_Am_Assert_Loser(S,G,iif) ) {
1218 * Set SPTbit(S,G) to TRUE
1219 * }
1220 * }
1221 */
1222void
1223pim_upstream_set_sptbit (struct pim_upstream *up, struct interface *incoming)
1224{
bacf3d12 1225 struct pim_upstream *starup = up->parent;
3a66b17b
DS
1226
1227 // iif == RPF_interfvace(S)
1228 if (up->rpf.source_nexthop.interface != incoming)
1229 {
1230 if (PIM_DEBUG_TRACE)
1231 zlog_debug ("%s: Incoming Interface: %s is different than RPF_interface(S) %s",
1232 __PRETTY_FUNCTION__, incoming->name, up->rpf.source_nexthop.interface->name);
1233 return;
1234 }
1235
1236 // AND JoinDesired(S,G) == TRUE
1237 // FIXME
1238
1239 // DirectlyConnected(S) == TRUE
1240 if (pim_if_connected_to_source (up->rpf.source_nexthop.interface, up->sg.src))
1241 {
1242 if (PIM_DEBUG_TRACE)
1243 zlog_debug ("%s: %s is directly connected to the source", __PRETTY_FUNCTION__,
8bfb8b67 1244 up->sg_str);
3a66b17b
DS
1245 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
1246 return;
1247 }
1248
1249 // OR RPF_interface(S) != RPF_interface(RP(G))
bacf3d12 1250 if (!starup || up->rpf.source_nexthop.interface != starup->rpf.source_nexthop.interface)
3a66b17b
DS
1251 {
1252 if (PIM_DEBUG_TRACE)
1253 zlog_debug ("%s: %s RPF_interface(S) != RPF_interface(RP(G))",
8bfb8b67 1254 __PRETTY_FUNCTION__, up->sg_str);
3a66b17b
DS
1255 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
1256 return;
1257 }
1258
1259 // OR inherited_olist(S,G,rpt) == NULL
1260 if (pim_upstream_is_sg_rpt(up) && pim_upstream_empty_inherited_olist(up))
1261 {
1262 if (PIM_DEBUG_TRACE)
1263 zlog_debug ("%s: %s OR inherited_olist(S,G,rpt) == NULL", __PRETTY_FUNCTION__,
8bfb8b67 1264 up->sg_str);
3a66b17b
DS
1265 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
1266 return;
1267 }
1268
1269 // OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
1270 // ( RPF'(S,G) != NULL ) )
1271 if (up->parent && pim_rpf_is_same (&up->rpf, &up->parent->rpf))
1272 {
1273 if (PIM_DEBUG_TRACE)
1274 zlog_debug ("%s: %s RPF'(S,G) is the same as RPF'(*,G)", __PRETTY_FUNCTION__,
8bfb8b67 1275 up->sg_str);
3a66b17b
DS
1276 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
1277 return;
1278 }
1279
1280 return;
1281}
80d9c3a0 1282
d7259eac 1283const char *
c9802954 1284pim_upstream_state2str (enum pim_upstream_state join_state)
d7259eac 1285{
c9802954 1286 switch (join_state)
d7259eac
DS
1287 {
1288 case PIM_UPSTREAM_NOTJOINED:
e775c0a4 1289 return "NotJoined";
d7259eac
DS
1290 break;
1291 case PIM_UPSTREAM_JOINED:
e775c0a4 1292 return "Joined";
d7259eac 1293 break;
d7259eac 1294 }
e775c0a4 1295 return "Unknown";
d7259eac 1296}
627ed2a3 1297
e0e127b0 1298const char *
0c2ebf00 1299pim_reg_state2str (enum pim_reg_state reg_state, char *state_str)
e0e127b0 1300{
0c2ebf00 1301 switch (reg_state)
e0e127b0 1302 {
0c2ebf00 1303 case PIM_REG_NOINFO:
1304 strcpy (state_str, "RegNoInfo");
e0e127b0 1305 break;
0c2ebf00 1306 case PIM_REG_JOIN:
1307 strcpy (state_str, "RegJoined");
e0e127b0 1308 break;
0c2ebf00 1309 case PIM_REG_JOIN_PENDING:
1310 strcpy (state_str, "RegJoinPend");
e0e127b0 1311 break;
0c2ebf00 1312 case PIM_REG_PRUNE:
1313 strcpy (state_str, "RegPrune");
e0e127b0 1314 break;
1315 default:
0c2ebf00 1316 strcpy (state_str, "RegUnknown");
e0e127b0 1317 }
1318 return state_str;
1319}
1320
627ed2a3
DS
1321static int
1322pim_upstream_register_stop_timer (struct thread *t)
1323{
4df01a4e 1324 struct pim_interface *pim_ifp;
627ed2a3
DS
1325 struct pim_upstream *up;
1326 struct pim_rpf *rpg;
1327 struct ip ip_hdr;
627ed2a3
DS
1328 up = THREAD_ARG (t);
1329
1330 up->t_rs_timer = NULL;
1331
1332 if (PIM_DEBUG_TRACE)
1333 {
0c2ebf00 1334 char state_str[PIM_REG_STATE_STR_LEN];
d5ed8a9c 1335 zlog_debug ("%s: (S,G)=%s upstream register stop timer %s",
8bfb8b67 1336 __PRETTY_FUNCTION__, up->sg_str,
0c2ebf00 1337 pim_reg_state2str(up->reg_state, state_str));
627ed2a3
DS
1338 }
1339
e0e127b0 1340 switch (up->reg_state)
627ed2a3 1341 {
0c2ebf00 1342 case PIM_REG_JOIN_PENDING:
1343 up->reg_state = PIM_REG_JOIN;
bb027ee8
DS
1344 pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
1345 break;
0c2ebf00 1346 case PIM_REG_JOIN:
627ed2a3 1347 break;
0c2ebf00 1348 case PIM_REG_PRUNE:
4df01a4e 1349 pim_ifp = up->rpf.source_nexthop.interface->info;
7ef66046
DS
1350 if (!pim_ifp)
1351 {
1352 if (PIM_DEBUG_TRACE)
1353 zlog_debug ("%s: Interface: %s is not configured for pim",
1354 __PRETTY_FUNCTION__, up->rpf.source_nexthop.interface->name);
1355 return 0;
1356 }
0c2ebf00 1357 up->reg_state = PIM_REG_JOIN_PENDING;
627ed2a3
DS
1358 pim_upstream_start_register_stop_timer (up, 1);
1359
70e7fda8
DS
1360 if (((up->channel_oil->cc.lastused/100) > PIM_KEEPALIVE_PERIOD) &&
1361 (I_am_RP (up->sg.grp)))
1362 {
1363 if (PIM_DEBUG_TRACE)
1364 zlog_debug ("%s: Stop sending the register, because I am the RP and we haven't seen a packet in a while", __PRETTY_FUNCTION__);
1365 return 0;
1366 }
4ed0af70 1367 rpg = RP (up->sg.grp);
627ed2a3
DS
1368 memset (&ip_hdr, 0, sizeof (struct ip));
1369 ip_hdr.ip_p = PIM_IP_PROTO_PIM;
1370 ip_hdr.ip_hl = 5;
1371 ip_hdr.ip_v = 4;
4ed0af70
DS
1372 ip_hdr.ip_src = up->sg.src;
1373 ip_hdr.ip_dst = up->sg.grp;
dc686f82 1374 ip_hdr.ip_len = htons (20);
627ed2a3 1375 // checksum is broken
4df01a4e 1376 pim_register_send ((uint8_t *)&ip_hdr, sizeof (struct ip),
0e3b3d5d 1377 pim_ifp->primary_address, rpg, 1, up);
627ed2a3
DS
1378 break;
1379 default:
1380 break;
1381 }
1382
1383 return 0;
1384}
1385
1386void
1387pim_upstream_start_register_stop_timer (struct pim_upstream *up, int null_register)
1388{
1389 uint32_t time;
1390
1391 if (up->t_rs_timer)
1392 {
1393 THREAD_TIMER_OFF (up->t_rs_timer);
1394 up->t_rs_timer = NULL;
1395 }
1396
1397 if (!null_register)
1398 {
1399 uint32_t lower = (0.5 * PIM_REGISTER_SUPPRESSION_PERIOD);
1400 uint32_t upper = (1.5 * PIM_REGISTER_SUPPRESSION_PERIOD);
1401 time = lower + (random () % (upper - lower + 1)) - PIM_REGISTER_PROBE_PERIOD;
1402 }
1403 else
1404 time = PIM_REGISTER_PROBE_PERIOD;
1405
1406 if (PIM_DEBUG_TRACE)
1407 {
05e451f8 1408 zlog_debug ("%s: (S,G)=%s Starting upstream register stop timer %d",
8bfb8b67 1409 __PRETTY_FUNCTION__, up->sg_str, time);
627ed2a3
DS
1410 }
1411 THREAD_TIMER_ON (master, up->t_rs_timer,
1412 pim_upstream_register_stop_timer,
1413 up, time);
1414}
4fdc8f36 1415
4fdc8f36 1416int
b5183fd1 1417pim_upstream_inherited_olist_decide (struct pim_upstream *up)
4fdc8f36 1418{
c8fc07cb 1419 struct interface *ifp;
1131c2eb 1420 struct pim_interface *pim_ifp = NULL;
c8fc07cb
DS
1421 struct pim_ifchannel *ch, *starch;
1422 struct listnode *node;
1423 struct pim_upstream *starup = up->parent;
219e0013
DS
1424 int output_intf = 0;
1425
1131c2eb
CS
1426 if (up->rpf.source_nexthop.interface)
1427 pim_ifp = up->rpf.source_nexthop.interface->info;
1428 else
1429 {
1430 if (PIM_DEBUG_TRACE)
1431 zlog_debug ("%s: up %s RPF is not present", __PRETTY_FUNCTION__, up->sg_str);
1432 }
da55afba 1433 if (pim_ifp && !up->channel_oil)
3667b0bc 1434 up->channel_oil = pim_channel_oil_add (&up->sg, pim_ifp->mroute_vif_index);
219e0013 1435
c8fc07cb 1436 for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp))
219e0013 1437 {
c8fc07cb
DS
1438 if (!ifp->info)
1439 continue;
219e0013 1440
c8fc07cb
DS
1441 ch = pim_ifchannel_find (ifp, &up->sg);
1442
1443 if (starup)
1444 starch = pim_ifchannel_find (ifp, &starup->sg);
1445 else
1446 starch = NULL;
1447
1448 if (!ch && !starch)
1449 continue;
1450
1451 if (pim_upstream_evaluate_join_desired_interface (up, ch, starch))
1452 {
781a1745
DS
1453 int flag = PIM_OIF_FLAG_PROTO_PIM;
1454
c8fc07cb 1455 if (!ch)
781a1745 1456 flag = PIM_OIF_FLAG_PROTO_STAR;
c8fc07cb
DS
1457
1458 pim_channel_add_oif (up->channel_oil, ifp, flag);
1459 output_intf++;
1460 }
219e0013
DS
1461 }
1462
b5183fd1
DS
1463 return output_intf;
1464}
1465
1466/*
1467 * For a given upstream, determine the inherited_olist
1468 * and apply it.
1469 *
1470 * inherited_olist(S,G,rpt) =
1471 * ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
1472 * (+) ( pim_include(*,G) (-) pim_exclude(S,G))
1473 * (-) ( lost_assert(*,G) (+) lost_assert(S,G,rpt) )
1474 *
1475 * inherited_olist(S,G) =
1476 * inherited_olist(S,G,rpt) (+)
1477 * joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
1478 *
1479 * return 1 if there are any output interfaces
1480 * return 0 if there are not any output interfaces
1481 */
1482int
1483pim_upstream_inherited_olist (struct pim_upstream *up)
1484{
1485 int output_intf = pim_upstream_inherited_olist_decide (up);
1486
483eef9d
DS
1487 /*
1488 * If we have output_intf switch state to Join and work like normal
1489 * If we don't have an output_intf that means we are probably a
1490 * switch on a stick so turn on forwarding to just accept the
1491 * incoming packets so we don't bother the other stuff!
1492 */
1493 if (output_intf)
1494 pim_upstream_switch (up, PIM_UPSTREAM_JOINED);
1495 else
1496 forward_on (up);
219e0013
DS
1497
1498 return output_intf;
4fdc8f36 1499}
d3dd1804 1500
80d9c3a0
DS
1501int
1502pim_upstream_empty_inherited_olist (struct pim_upstream *up)
1503{
ce0ddb4e 1504 return pim_channel_oil_empty (up->channel_oil);
80d9c3a0
DS
1505}
1506
d3dd1804
DS
1507/*
1508 * When we have a new neighbor,
1509 * find upstreams that don't have their rpf_addr
1510 * set and see if the new neighbor allows
1511 * the join to be sent
1512 */
1513void
1514pim_upstream_find_new_rpf (void)
1515{
1516 struct listnode *up_node;
1517 struct listnode *up_nextnode;
1518 struct pim_upstream *up;
1519
1520 /*
0f588989
DS
1521 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
1522 */
1523 for (ALL_LIST_ELEMENTS(pim_upstream_list, up_node, up_nextnode, up))
d3dd1804 1524 {
63c59d0c 1525 if (pim_rpf_addr_is_inaddr_any(&up->rpf))
d3dd1804 1526 {
3197cb1e 1527 if (PIM_DEBUG_TRACE)
d3dd1804 1528 zlog_debug ("Upstream %s without a path to send join, checking",
8bfb8b67 1529 up->sg_str);
1bc98276 1530 pim_rpf_update (up, NULL, 1);
d3dd1804
DS
1531 }
1532 }
1533}
0f588989 1534
0f588989
DS
1535static unsigned int
1536pim_upstream_hash_key (void *arg)
1537{
1538 struct pim_upstream *up = (struct pim_upstream *)arg;
1539
1540 return jhash_2words (up->sg.src.s_addr, up->sg.grp.s_addr, 0);
1541}
1542
1543void pim_upstream_terminate (void)
1544{
1545 if (pim_upstream_list)
693c9259 1546 list_delete (pim_upstream_list);
0f588989
DS
1547 pim_upstream_list = NULL;
1548
1549 if (pim_upstream_hash)
1550 hash_free (pim_upstream_hash);
693c9259 1551 pim_upstream_hash = NULL;
0f588989
DS
1552}
1553
1554static int
1555pim_upstream_equal (const void *arg1, const void *arg2)
1556{
1557 const struct pim_upstream *up1 = (const struct pim_upstream *)arg1;
1558 const struct pim_upstream *up2 = (const struct pim_upstream *)arg2;
1559
1560 if ((up1->sg.grp.s_addr == up2->sg.grp.s_addr) &&
1561 (up1->sg.src.s_addr == up2->sg.src.s_addr))
1562 return 1;
1563
1564 return 0;
1565}
1566
1bf16443 1567/* rfc4601:section-4.2:"Data Packet Forwarding Rules" defines
1568 * the cases where kat has to be restarted on rxing traffic -
1569 *
1570 * if( DirectlyConnected(S) == TRUE AND iif == RPF_interface(S) ) {
1571 * set KeepaliveTimer(S,G) to Keepalive_Period
1572 * # Note: a register state transition or UpstreamJPState(S,G)
1573 * # transition may happen as a result of restarting
1574 * # KeepaliveTimer, and must be dealt with here.
1575 * }
1576 * if( iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined AND
1577 * inherited_olist(S,G) != NULL ) {
1578 * set KeepaliveTimer(S,G) to Keepalive_Period
1579 * }
1580 */
1581static bool pim_upstream_kat_start_ok(struct pim_upstream *up)
1582{
1583 /* "iif == RPF_interface(S)" check has to be done by the kernel or hw
1584 * so we will skip that here */
1585 if (pim_if_connected_to_source(up->rpf.source_nexthop.interface,
1586 up->sg.src)) {
1587 return true;
1588 }
1589
1590 if ((up->join_state == PIM_UPSTREAM_JOINED) &&
1591 !pim_upstream_empty_inherited_olist(up)) {
1592 /* XXX: I have added this RP check just for 3.2 and it's a digression from
1593 * what rfc-4601 says. Till now we were only running KAT on FHR and RP and
1594 * there is some angst around making the change to run it all routers that
1595 * maintain the (S, G) state. This is tracked via CM-13601 and MUST be
1596 * removed to handle spt turn-arounds correctly in a 3-tier clos */
1597 if (I_am_RP (up->sg.grp))
1598 return true;
1599 }
1600
1601 return false;
1602}
1603
9c5e4d62
DS
1604/*
1605 * Code to check and see if we've received packets on a S,G mroute
1606 * and if so to set the SPT bit appropriately
1607 */
1608static void
1609pim_upstream_sg_running (void *arg)
1610{
1611 struct pim_upstream *up = (struct pim_upstream *)arg;
e43b8697
DS
1612
1613 // No packet can have arrived here if this is the case
1614 if (!up->channel_oil || !up->channel_oil->installed)
1615 {
1616 if (PIM_DEBUG_TRACE)
1617 zlog_debug ("%s: %s is not installed in mroute",
8bfb8b67 1618 __PRETTY_FUNCTION__, up->sg_str);
e43b8697
DS
1619 return;
1620 }
1621
bfdf4096
DS
1622 /*
1623 * This is a bit of a hack
1624 * We've noted that we should rescan but
1625 * we've missed the window for doing so in
1626 * pim_zebra.c for some reason. I am
1627 * only doing this at this point in time
1628 * to get us up and working for the moment
1629 */
1630 if (up->channel_oil->oil_inherited_rescan)
1631 {
1632 if (PIM_DEBUG_TRACE)
1633 zlog_debug ("%s: Handling unscanned inherited_olist for %s", __PRETTY_FUNCTION__, up->sg_str);
1634 pim_upstream_inherited_olist_decide (up);
1635 up->channel_oil->oil_inherited_rescan = 0;
1636 }
e43b8697
DS
1637 pim_mroute_update_counters (up->channel_oil);
1638
1639 // Have we seen packets?
1bf16443 1640 if ((up->channel_oil->cc.oldpktcnt >= up->channel_oil->cc.pktcnt) &&
e43b8697
DS
1641 (up->channel_oil->cc.lastused/100 > 30))
1642 {
1643 if (PIM_DEBUG_TRACE)
1644 {
eeedae06
DS
1645 zlog_debug ("%s: %s old packet count is equal or lastused is greater than 30, (%ld,%ld,%lld)",
1646 __PRETTY_FUNCTION__, up->sg_str,
1647 up->channel_oil->cc.oldpktcnt,
1648 up->channel_oil->cc.pktcnt,
1649 up->channel_oil->cc.lastused/100);
e43b8697
DS
1650 }
1651 return;
1652 }
1653
850a9f99 1654 if (pim_upstream_kat_start_ok(up))
1bf16443 1655 {
850a9f99
DS
1656 /* Add a source reference to the stream if
1657 * one doesn't already exist */
1658 if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags))
1659 {
1660 if (PIM_DEBUG_TRACE)
1661 zlog_debug ("source reference created on kat restart %s", up->sg_str);
8bfb8b67 1662
850a9f99
DS
1663 pim_upstream_ref(up, PIM_UPSTREAM_FLAG_MASK_SRC_STREAM);
1664 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
1665 pim_upstream_fhr_kat_start(up);
1666 }
1667 pim_upstream_keep_alive_timer_start(up, qpim_keep_alive_time);
1bf16443 1668 }
850a9f99 1669 else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up->flags))
1bf16443 1670 pim_upstream_keep_alive_timer_start(up, qpim_keep_alive_time);
1bf16443 1671
1672 if (up->sptbit != PIM_UPSTREAM_SPTBIT_TRUE)
850a9f99
DS
1673 {
1674 pim_upstream_set_sptbit(up, up->rpf.source_nexthop.interface);
1675 }
e43b8697 1676 return;
9c5e4d62
DS
1677}
1678
a7b2b1e2
DS
1679void
1680pim_upstream_add_lhr_star_pimreg (void)
1681{
1682 struct pim_upstream *up;
1683 struct listnode *node;
1684
1685 for (ALL_LIST_ELEMENTS_RO (pim_upstream_list, node, up))
1686 {
1687 if (up->sg.src.s_addr != INADDR_ANY)
1688 continue;
1689
1690 if (!PIM_UPSTREAM_FLAG_TEST_SRC_IGMP (up->flags))
1691 continue;
1692
1693 pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_IGMP);
1694 }
1695}
1696
1697void
df94f9a9
DS
1698pim_upstream_spt_prefix_list_update (struct prefix_list *pl)
1699{
1700 const char *pname = prefix_list_name (pl);
1701
1702 if (pimg->spt.plist && strcmp (pimg->spt.plist, pname) == 0)
1703 {
1704 pim_upstream_remove_lhr_star_pimreg (pname);
1705 }
1706}
1707
1708/*
1709 * nlist -> The new prefix list
1710 *
1711 * Per Group Application of pimreg to the OIL
1712 * If the prefix list tells us DENY then
1713 * we need to Switchover to SPT immediate
1714 * so add the pimreg.
1715 * If the prefix list tells us to ACCEPT than
1716 * we need to Never do the SPT so remove
1717 * the interface
1718 *
1719 */
1720void
1721pim_upstream_remove_lhr_star_pimreg (const char *nlist)
a7b2b1e2
DS
1722{
1723 struct pim_upstream *up;
1724 struct listnode *node;
df94f9a9
DS
1725 struct prefix_list *np;
1726 struct prefix g;
1727 enum prefix_list_type apply_new;
1728
1729 np = prefix_list_lookup (AFI_IP, nlist);
1730
1731 g.family = AF_INET;
1732 g.prefixlen = IPV4_MAX_PREFIXLEN;
a7b2b1e2
DS
1733
1734 for (ALL_LIST_ELEMENTS_RO (pim_upstream_list, node, up))
1735 {
1736 if (up->sg.src.s_addr != INADDR_ANY)
1737 continue;
1738
1739 if (!PIM_UPSTREAM_FLAG_TEST_SRC_IGMP (up->flags))
1740 continue;
1741
df94f9a9
DS
1742 if (!nlist)
1743 {
1744 pim_channel_del_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_IGMP);
1745 continue;
1746 }
1747 g.u.prefix4 = up->sg.grp;
1748 apply_new = prefix_list_apply (np, &g);
1749 if (apply_new == PREFIX_DENY)
1750 pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_IGMP);
1751 else
1752 pim_channel_del_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_IGMP);
a7b2b1e2
DS
1753 }
1754}
1755
040d86ad
DS
1756void
1757pim_upstream_init (void)
0f588989 1758{
9c5e4d62
DS
1759 pim_upstream_sg_wheel = wheel_init (master, 31000, 100,
1760 pim_upstream_hash_key,
1761 pim_upstream_sg_running);
0f588989
DS
1762 pim_upstream_hash = hash_create_size (8192, pim_upstream_hash_key,
1763 pim_upstream_equal);
1764
1765 pim_upstream_list = list_new ();
1766 pim_upstream_list->del = (void (*)(void *)) pim_upstream_free;
03417ccd 1767 pim_upstream_list->cmp = pim_upstream_compare;
0f588989
DS
1768
1769}