]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_upstream.c
pimd: Fix possible crash using 'struct pim_nexthop'
[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
19
12e41d03
DL
20*/
21
22#include <zebra.h>
23
24#include "zebra/rib.h"
25
26#include "log.h"
27#include "zclient.h"
28#include "memory.h"
29#include "thread.h"
30#include "linklist.h"
dfe43e25
DW
31#include "vty.h"
32#include "plist.h"
0f588989
DS
33#include "hash.h"
34#include "jhash.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"
12e41d03 53
0f588989
DS
54struct hash *pim_upstream_hash = NULL;
55struct list *pim_upstream_list = NULL;
56
12e41d03
DL
57static void join_timer_start(struct pim_upstream *up);
58static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up);
59
cfa91a87
DS
60/*
61 * A (*,G) or a (*,*) is going away
62 * remove the parent pointer from
63 * those pointing at us
64 */
65static void
66pim_upstream_remove_children (struct pim_upstream *up)
67{
68 struct listnode *ch_node;
69 struct pim_upstream *child;
70
71 // Basic sanity, (*,*) not currently supported
4ed0af70
DS
72 if ((up->sg.src.s_addr == INADDR_ANY) &&
73 (up->sg.grp.s_addr == INADDR_ANY))
cfa91a87
DS
74 return;
75
76 // Basic sanity (S,G) have no children
4ed0af70
DS
77 if ((up->sg.src.s_addr != INADDR_ANY) &&
78 (up->sg.grp.s_addr != INADDR_ANY))
cfa91a87
DS
79 return;
80
0f588989 81 for (ALL_LIST_ELEMENTS_RO (pim_upstream_list, ch_node, child))
cfa91a87
DS
82 {
83 if (child->parent == up)
84 child->parent = NULL;
85 }
86}
87
88/*
89 * A (*,G) or a (*,*) is being created
90 * Find the children that would point
91 * at us.
92 */
93static void
94pim_upstream_find_new_children (struct pim_upstream *up)
95{
96 struct pim_upstream *child;
97 struct listnode *ch_node;
98
4ed0af70
DS
99 if ((up->sg.src.s_addr != INADDR_ANY) &&
100 (up->sg.grp.s_addr != INADDR_ANY))
cfa91a87
DS
101 return;
102
4ed0af70
DS
103 if ((up->sg.src.s_addr == INADDR_ANY) &&
104 (up->sg.grp.s_addr == INADDR_ANY))
cfa91a87
DS
105 return;
106
0f588989 107 for (ALL_LIST_ELEMENTS_RO (pim_upstream_list, ch_node, child))
cfa91a87 108 {
4ed0af70 109 if ((up->sg.grp.s_addr != INADDR_ANY) &&
0f588989 110 (child->sg.grp.s_addr == up->sg.grp.s_addr) &&
cfa91a87
DS
111 (child != up))
112 child->parent = up;
113 }
114}
115
4d99418b
DS
116/*
117 * If we have a (*,*) || (S,*) there is no parent
118 * If we have a (S,G), find the (*,G)
119 * If we have a (*,G), find the (*,*)
120 */
121static struct pim_upstream *
4ed0af70 122pim_upstream_find_parent (struct prefix_sg *sg)
4d99418b 123{
4ed0af70 124 struct prefix_sg any = *sg;
4d99418b
DS
125
126 // (*,*) || (S,*)
4ed0af70
DS
127 if (((sg->src.s_addr == INADDR_ANY) &&
128 (sg->grp.s_addr == INADDR_ANY)) ||
129 ((sg->src.s_addr != INADDR_ANY) &&
130 (sg->grp.s_addr == INADDR_ANY)))
4d99418b
DS
131 return NULL;
132
133 // (S,G)
4ed0af70
DS
134 if ((sg->src.s_addr != INADDR_ANY) &&
135 (sg->grp.s_addr != INADDR_ANY))
4d99418b 136 {
4ed0af70 137 any.src.s_addr = INADDR_ANY;
4d99418b
DS
138 return pim_upstream_find (&any);
139 }
140
141 // (*,G)
4ed0af70 142 any.grp.s_addr = INADDR_ANY;
4d99418b
DS
143 return pim_upstream_find (&any);
144}
145
12e41d03
DL
146void pim_upstream_free(struct pim_upstream *up)
147{
148 XFREE(MTYPE_PIM_UPSTREAM, up);
149}
150
151static void upstream_channel_oil_detach(struct pim_upstream *up)
152{
153 if (up->channel_oil) {
154 pim_channel_oil_del(up->channel_oil);
25a335e0 155 up->channel_oil = NULL;
12e41d03
DL
156 }
157}
158
e5905a3b
DS
159void
160pim_upstream_del(struct pim_upstream *up, const char *name)
12e41d03 161{
e5905a3b
DS
162 if (PIM_DEBUG_PIM_TRACE)
163 {
164 zlog_debug ("%s: Delete (%s) ref count: %d",
165 name, pim_str_sg_dump (&up->sg), up->ref_count);
166 }
167 --up->ref_count;
168
169 if (up->ref_count >= 1)
170 return;
171
594a78cc
DS
172 if (PIM_DEBUG_PIM_TRACE)
173 zlog_debug ("%s: %s is being deleted",
174 __PRETTY_FUNCTION__,
175 pim_str_sg_dump (&up->sg));
12e41d03 176 THREAD_OFF(up->t_join_timer);
f14248dd 177 THREAD_OFF(up->t_ka_timer);
792f4d29 178 THREAD_OFF(up->t_rs_timer);
12e41d03 179
cfa91a87 180 pim_upstream_remove_children (up);
7fe1f662 181 pim_mroute_del (up->channel_oil);
12e41d03
DL
182 upstream_channel_oil_detach(up);
183
184 /*
185 notice that listnode_delete() can't be moved
186 into pim_upstream_free() because the later is
187 called by list_delete_all_node()
188 */
0f588989
DS
189 listnode_delete (pim_upstream_list, up);
190 hash_release (pim_upstream_hash, up);
12e41d03
DL
191
192 pim_upstream_free(up);
193}
194
56638739
DS
195void
196pim_upstream_send_join (struct pim_upstream *up)
12e41d03 197{
12e41d03 198 if (PIM_DEBUG_PIM_TRACE) {
eaa54bdb 199 char rpf_str[PREFIX_STRLEN];
63c59d0c 200 pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
05e451f8 201 zlog_debug ("%s: RPF'%s=%s(%s) for Interface %s", __PRETTY_FUNCTION__,
c9802954 202 pim_str_sg_dump (&up->sg), rpf_str, pim_upstream_state2str (up->join_state),
56638739 203 up->rpf.source_nexthop.interface->name);
63c59d0c 204 if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
05e451f8 205 zlog_debug("%s: can't send join upstream: RPF'%s=%s",
56638739 206 __PRETTY_FUNCTION__,
05e451f8 207 pim_str_sg_dump (&up->sg), rpf_str);
12e41d03
DL
208 /* warning only */
209 }
210 }
56638739 211
12e41d03
DL
212 /* send Join(S,G) to the current upstream neighbor */
213 pim_joinprune_send(up->rpf.source_nexthop.interface,
63c59d0c 214 up->rpf.rpf_addr.u.prefix4,
05e451f8 215 &up->sg,
12e41d03
DL
216 1 /* join */);
217}
218
219static int on_join_timer(struct thread *t)
220{
221 struct pim_upstream *up;
222
223 zassert(t);
224 up = THREAD_ARG(t);
225 zassert(up);
226
bb6e291f
DS
227 up->t_join_timer = NULL;
228
229 /*
230 * In the case of a HFR we will not ahve anyone to send this to.
231 */
0bf27c5c 232 if (PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
bb6e291f
DS
233 return 0;
234
eb345962
DS
235 /*
236 * Don't send the join if the outgoing interface is a loopback
237 * But since this might change leave the join timer running
238 */
239 if (!if_is_loopback (up->rpf.source_nexthop.interface))
240 pim_upstream_send_join (up);
12e41d03 241
12e41d03
DL
242 join_timer_start(up);
243
244 return 0;
245}
246
247static void join_timer_start(struct pim_upstream *up)
248{
249 if (PIM_DEBUG_PIM_EVENTS) {
05e451f8 250 zlog_debug("%s: starting %d sec timer for upstream (S,G)=%s",
12e41d03
DL
251 __PRETTY_FUNCTION__,
252 qpim_t_periodic,
05e451f8 253 pim_str_sg_dump (&up->sg));
12e41d03
DL
254 }
255
8e38a2cf 256 THREAD_OFF (up->t_join_timer);
12e41d03
DL
257 THREAD_TIMER_ON(master, up->t_join_timer,
258 on_join_timer,
259 up, qpim_t_periodic);
260}
261
262void pim_upstream_join_timer_restart(struct pim_upstream *up)
263{
264 THREAD_OFF(up->t_join_timer);
265 join_timer_start(up);
266}
267
268static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up,
269 int interval_msec)
270{
271 if (PIM_DEBUG_PIM_EVENTS) {
05e451f8 272 zlog_debug("%s: restarting %d msec timer for upstream (S,G)=%s",
12e41d03
DL
273 __PRETTY_FUNCTION__,
274 interval_msec,
05e451f8 275 pim_str_sg_dump (&up->sg));
12e41d03
DL
276 }
277
278 THREAD_OFF(up->t_join_timer);
279 THREAD_TIMER_MSEC_ON(master, up->t_join_timer,
280 on_join_timer,
281 up, interval_msec);
282}
283
284void pim_upstream_join_suppress(struct pim_upstream *up,
285 struct in_addr rpf_addr,
286 int holdtime)
287{
288 long t_joinsuppress_msec;
289 long join_timer_remain_msec;
290
291 t_joinsuppress_msec = MIN(pim_if_t_suppressed_msec(up->rpf.source_nexthop.interface),
292 1000 * holdtime);
293
294 join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
295
296 if (PIM_DEBUG_PIM_TRACE) {
eaa54bdb 297 char rpf_str[INET_ADDRSTRLEN];
12e41d03 298 pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
05e451f8 299 zlog_debug("%s %s: detected Join%s to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
12e41d03 300 __FILE__, __PRETTY_FUNCTION__,
05e451f8 301 pim_str_sg_dump (&up->sg),
12e41d03
DL
302 rpf_str,
303 join_timer_remain_msec, t_joinsuppress_msec);
304 }
305
306 if (join_timer_remain_msec < t_joinsuppress_msec) {
307 if (PIM_DEBUG_PIM_TRACE) {
05e451f8 308 zlog_debug("%s %s: suppressing Join(S,G)=%s for %ld msec",
12e41d03 309 __FILE__, __PRETTY_FUNCTION__,
05e451f8 310 pim_str_sg_dump (&up->sg), t_joinsuppress_msec);
12e41d03
DL
311 }
312
313 pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec);
314 }
315}
316
317void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
318 struct pim_upstream *up,
319 struct in_addr rpf_addr)
320{
321 long join_timer_remain_msec;
322 int t_override_msec;
323
324 join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
325 t_override_msec = pim_if_t_override_msec(up->rpf.source_nexthop.interface);
326
327 if (PIM_DEBUG_PIM_TRACE) {
eaa54bdb 328 char rpf_str[INET_ADDRSTRLEN];
12e41d03 329 pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
05e451f8 330 zlog_debug("%s: to RPF'%s=%s: join_timer=%ld msec t_override=%d msec",
12e41d03 331 debug_label,
05e451f8 332 pim_str_sg_dump (&up->sg), rpf_str,
12e41d03
DL
333 join_timer_remain_msec, t_override_msec);
334 }
335
336 if (join_timer_remain_msec > t_override_msec) {
337 if (PIM_DEBUG_PIM_TRACE) {
05e451f8 338 zlog_debug("%s: decreasing (S,G)=%s join timer to t_override=%d msec",
12e41d03 339 debug_label,
05e451f8 340 pim_str_sg_dump (&up->sg),
12e41d03
DL
341 t_override_msec);
342 }
343
344 pim_upstream_join_timer_restart_msec(up, t_override_msec);
345 }
346}
347
348static void forward_on(struct pim_upstream *up)
349{
350 struct listnode *ifnode;
351 struct listnode *ifnextnode;
352 struct listnode *chnode;
353 struct listnode *chnextnode;
354 struct interface *ifp;
355 struct pim_interface *pim_ifp;
356 struct pim_ifchannel *ch;
357
358 /* scan all interfaces */
469351b3 359 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
360 pim_ifp = ifp->info;
361 if (!pim_ifp)
362 continue;
363
364 /* scan per-interface (S,G) state */
365 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
366
367 if (ch->upstream != up)
368 continue;
369
370 if (pim_macro_chisin_oiflist(ch))
371 pim_forward_start(ch);
372
373 } /* scan iface channel list */
374 } /* scan iflist */
375}
376
377static void forward_off(struct pim_upstream *up)
378{
379 struct listnode *ifnode;
380 struct listnode *ifnextnode;
381 struct listnode *chnode;
382 struct listnode *chnextnode;
383 struct interface *ifp;
384 struct pim_interface *pim_ifp;
385 struct pim_ifchannel *ch;
386
387 /* scan all interfaces */
469351b3 388 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
389 pim_ifp = ifp->info;
390 if (!pim_ifp)
391 continue;
392
393 /* scan per-interface (S,G) state */
394 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
395
396 if (ch->upstream != up)
397 continue;
398
399 pim_forward_stop(ch);
400
401 } /* scan iface channel list */
402 } /* scan iflist */
403}
404
bb6e291f
DS
405static int
406pim_upstream_could_register (struct pim_upstream *up)
407{
408 struct pim_interface *pim_ifp = up->rpf.source_nexthop.interface->info;
409
35917570 410 if (pim_ifp && PIM_I_am_DR (pim_ifp) &&
bb6e291f
DS
411 pim_if_connected_to_source (up->rpf.source_nexthop.interface, up->sg.src))
412 return 1;
413
414 return 0;
415}
416
7fcdfb34
DS
417void
418pim_upstream_switch(struct pim_upstream *up,
419 enum pim_upstream_state new_state)
12e41d03
DL
420{
421 enum pim_upstream_state old_state = up->join_state;
422
12e41d03 423 if (PIM_DEBUG_PIM_EVENTS) {
c9802954 424 zlog_debug("%s: PIM_UPSTREAM_%s: (S,G) old: %s new: %s",
12e41d03 425 __PRETTY_FUNCTION__,
c9802954
DS
426 pim_str_sg_dump (&up->sg),
427 pim_upstream_state2str (up->join_state),
428 pim_upstream_state2str (new_state));
12e41d03
DL
429 }
430
bb027ee8
DS
431 /*
432 * This code still needs work.
433 */
434 switch (up->join_state)
435 {
436 case PIM_UPSTREAM_PRUNE:
0bf27c5c 437 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
7747bad6
DS
438 {
439 up->join_state = new_state;
440 up->state_transition = pim_time_monotonic_sec ();
441 }
442 break;
bb027ee8
DS
443 case PIM_UPSTREAM_JOIN_PENDING:
444 break;
445 case PIM_UPSTREAM_NOTJOINED:
446 case PIM_UPSTREAM_JOINED:
447 up->join_state = new_state;
448 up->state_transition = pim_time_monotonic_sec();
449
450 break;
451 }
452
12e41d03
DL
453 pim_upstream_update_assert_tracking_desired(up);
454
455 if (new_state == PIM_UPSTREAM_JOINED) {
81900c5a
DS
456 if (old_state != PIM_UPSTREAM_JOINED)
457 {
0bf27c5c 458 int old_fhr = PIM_UPSTREAM_FLAG_TEST_FHR(up->flags);
81900c5a 459 forward_on(up);
0bf27c5c 460 if (pim_upstream_could_register (up))
bb6e291f 461 {
0bf27c5c 462 PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
a9b59879 463 if (!old_fhr && PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags))
bb6e291f
DS
464 {
465 pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
466 pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
467 }
468 }
469 else
470 {
471 pim_upstream_send_join (up);
472 join_timer_start (up);
473 }
81900c5a
DS
474 }
475 else
476 {
477 forward_on (up);
478 }
12e41d03
DL
479 }
480 else {
481 forward_off(up);
482 pim_joinprune_send(up->rpf.source_nexthop.interface,
63c59d0c 483 up->rpf.rpf_addr.u.prefix4,
05e451f8 484 &up->sg,
12e41d03 485 0 /* prune */);
7fcdfb34
DS
486 if (up->t_join_timer)
487 THREAD_OFF(up->t_join_timer);
12e41d03 488 }
12e41d03
DL
489}
490
4ed0af70 491static struct pim_upstream *pim_upstream_new(struct prefix_sg *sg,
4a40c37a
DS
492 struct interface *incoming,
493 int flags)
12e41d03
DL
494{
495 struct pim_upstream *up;
2f702571 496 enum pim_rpf_result rpf_result;
12e41d03 497
36d9e7dc 498 up = XCALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
12e41d03 499 if (!up) {
36d9e7dc 500 zlog_err("%s: PIM XCALLOC(%zu) failure",
12e41d03 501 __PRETTY_FUNCTION__, sizeof(*up));
8f5f5e91 502 return NULL;
12e41d03
DL
503 }
504
5074a423 505 up->sg = *sg;
0f588989 506 up = hash_get (pim_upstream_hash, up, hash_alloc_intern);
36d6bd7d 507 if (!pim_rp_set_upstream_addr (&up->upstream_addr, sg->src, sg->grp))
8f5f5e91
DS
508 {
509 if (PIM_DEBUG_PIM_TRACE)
510 zlog_debug("%s: Received a (*,G) with no RP configured", __PRETTY_FUNCTION__);
511
512 XFREE (MTYPE_PIM_UPSTREAM, up);
513 return NULL;
514 }
515
4d99418b 516 up->parent = pim_upstream_find_parent (sg);
cfa91a87 517 pim_upstream_find_new_children (up);
4a40c37a 518 up->flags = flags;
12e41d03 519 up->ref_count = 1;
4a4c4a07
DS
520 up->t_join_timer = NULL;
521 up->t_ka_timer = NULL;
792f4d29 522 up->t_rs_timer = NULL;
12e41d03
DL
523 up->join_state = 0;
524 up->state_transition = pim_time_monotonic_sec();
4a4c4a07 525 up->channel_oil = NULL;
f9e0ab5b 526 up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE;
12e41d03 527
f24405b1 528 up->rpf.source_nexthop.interface = NULL;
63c59d0c
DS
529 up->rpf.source_nexthop.mrib_nexthop_addr.family = AF_INET;
530 up->rpf.source_nexthop.mrib_nexthop_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY;
12e41d03
DL
531 up->rpf.source_nexthop.mrib_metric_preference = qpim_infinite_assert_metric.metric_preference;
532 up->rpf.source_nexthop.mrib_route_metric = qpim_infinite_assert_metric.route_metric;
63c59d0c
DS
533 up->rpf.rpf_addr.family = AF_INET;
534 up->rpf.rpf_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY;
12e41d03 535
d3dd1804 536 rpf_result = pim_rpf_update(up, NULL);
2f702571 537 if (rpf_result == PIM_RPF_FAILURE) {
4a40c37a
DS
538 if (PIM_DEBUG_PIM_TRACE)
539 zlog_debug ("%s: Attempting to create upstream(%s), Unable to RPF for source", __PRETTY_FUNCTION__,
540 pim_str_sg_dump (&up->sg));
2f702571
DS
541 XFREE(MTYPE_PIM_UPSTREAM, up);
542 return NULL;
543 }
12e41d03 544
0f588989 545 listnode_add_sort(pim_upstream_list, up);
12e41d03
DL
546
547 return up;
548}
549
4ed0af70 550struct pim_upstream *pim_upstream_find(struct prefix_sg *sg)
12e41d03 551{
0f588989
DS
552 struct pim_upstream lookup;
553 struct pim_upstream *up = NULL;
12e41d03 554
0f588989
DS
555 lookup.sg = *sg;
556 up = hash_lookup (pim_upstream_hash, &lookup);
557 return up;
12e41d03
DL
558}
559
4ed0af70 560struct pim_upstream *pim_upstream_add(struct prefix_sg *sg,
4a40c37a 561 struct interface *incoming,
e5905a3b 562 int flags, const char *name)
12e41d03 563{
594a78cc 564 struct pim_upstream *up = NULL;
e5905a3b 565 int found = 0;
5074a423 566 up = pim_upstream_find(sg);
12e41d03
DL
567 if (up) {
568 ++up->ref_count;
16b72591 569 up->flags |= flags;
e5905a3b 570 found = 1;
12e41d03
DL
571 }
572 else {
4a40c37a 573 up = pim_upstream_new(sg, incoming, flags);
12e41d03
DL
574 }
575
e5905a3b
DS
576 if (PIM_DEBUG_TRACE)
577 zlog_debug("%s(%s): (%s), found: %d: ref_count: %d",
578 __PRETTY_FUNCTION__, name,
579 pim_str_sg_dump (&up->sg), found,
580 up->ref_count);
12e41d03 581
e5905a3b 582 return up;
12e41d03
DL
583}
584
7a3ddda5
DS
585static int
586pim_upstream_evaluate_join_desired_interface (struct pim_upstream *up,
587 struct pim_ifchannel *ch)
588{
589 struct pim_upstream *parent = up->parent;
590
591 if (ch->upstream == up)
592 {
593 if (!pim_macro_ch_lost_assert(ch) && pim_macro_chisin_joins_or_include(ch))
594 return 1;
595 }
596 /*
597 * joins (*,G)
598 */
599 if (parent && ch->upstream == parent)
600 {
601 if (!pim_macro_ch_lost_assert (ch) && pim_macro_chisin_joins_or_include (ch))
602 return 1;
603 }
604
605 return 0;
606}
607
12e41d03
DL
608/*
609 Evaluate JoinDesired(S,G):
610
611 JoinDesired(S,G) is true if there is a downstream (S,G) interface I
612 in the set:
613
614 inherited_olist(S,G) =
615 joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
616
617 JoinDesired(S,G) may be affected by changes in the following:
618
619 pim_ifp->primary_address
620 pim_ifp->pim_dr_addr
621 ch->ifassert_winner_metric
622 ch->ifassert_winner
623 ch->local_ifmembership
624 ch->ifjoin_state
625 ch->upstream->rpf.source_nexthop.mrib_metric_preference
626 ch->upstream->rpf.source_nexthop.mrib_route_metric
627 ch->upstream->rpf.source_nexthop.interface
628
629 See also pim_upstream_update_join_desired() below.
630 */
631int pim_upstream_evaluate_join_desired(struct pim_upstream *up)
632{
633 struct listnode *ifnode;
634 struct listnode *ifnextnode;
635 struct listnode *chnode;
636 struct listnode *chnextnode;
637 struct interface *ifp;
638 struct pim_interface *pim_ifp;
639 struct pim_ifchannel *ch;
7a3ddda5 640 int ret = 0;
12e41d03
DL
641
642 /* scan all interfaces */
469351b3 643 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
644 pim_ifp = ifp->info;
645 if (!pim_ifp)
646 continue;
647
648 /* scan per-interface (S,G) state */
7a3ddda5
DS
649 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch))
650 {
651 ret += pim_upstream_evaluate_join_desired_interface (up, ch);
652 } /* scan iface channel list */
12e41d03
DL
653 } /* scan iflist */
654
7a3ddda5 655 return ret; /* false */
12e41d03
DL
656}
657
658/*
659 See also pim_upstream_evaluate_join_desired() above.
660*/
661void pim_upstream_update_join_desired(struct pim_upstream *up)
662{
663 int was_join_desired; /* boolean */
664 int is_join_desired; /* boolean */
665
666 was_join_desired = PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags);
667
668 is_join_desired = pim_upstream_evaluate_join_desired(up);
669 if (is_join_desired)
670 PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up->flags);
671 else
672 PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up->flags);
673
674 /* switched from false to true */
675 if (is_join_desired && !was_join_desired) {
12e41d03
DL
676 pim_upstream_switch(up, PIM_UPSTREAM_JOINED);
677 return;
678 }
679
680 /* switched from true to false */
681 if (!is_join_desired && was_join_desired) {
12e41d03
DL
682 pim_upstream_switch(up, PIM_UPSTREAM_NOTJOINED);
683 return;
684 }
685}
686
687/*
688 RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
689 Transitions from Joined State
690 RPF'(S,G) GenID changes
691
692 The upstream (S,G) state machine remains in Joined state. If the
693 Join Timer is set to expire in more than t_override seconds, reset
694 it so that it expires after t_override seconds.
695*/
696void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr)
697{
698 struct listnode *up_node;
699 struct listnode *up_nextnode;
700 struct pim_upstream *up;
701
702 /*
0f588989
DS
703 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
704 */
705 for (ALL_LIST_ELEMENTS(pim_upstream_list, up_node, up_nextnode, up)) {
12e41d03
DL
706
707 if (PIM_DEBUG_PIM_TRACE) {
eaa54bdb
DW
708 char neigh_str[INET_ADDRSTRLEN];
709 char rpf_addr_str[PREFIX_STRLEN];
12e41d03 710 pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
63c59d0c 711 pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
05e451f8 712 zlog_debug("%s: matching neigh=%s against upstream (S,G)=%s joined=%d rpf_addr=%s",
12e41d03 713 __PRETTY_FUNCTION__,
05e451f8 714 neigh_str, pim_str_sg_dump (&up->sg),
12e41d03
DL
715 up->join_state == PIM_UPSTREAM_JOINED,
716 rpf_addr_str);
717 }
718
719 /* consider only (S,G) upstream in Joined state */
720 if (up->join_state != PIM_UPSTREAM_JOINED)
721 continue;
722
723 /* match RPF'(S,G)=neigh_addr */
63c59d0c 724 if (up->rpf.rpf_addr.u.prefix4.s_addr != neigh_addr.s_addr)
12e41d03
DL
725 continue;
726
727 pim_upstream_join_timer_decrease_to_t_override("RPF'(S,G) GenID change",
728 up, neigh_addr);
729 }
730}
731
732
733void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
734 struct interface *old_rpf_ifp)
735{
736 struct listnode *ifnode;
737 struct listnode *ifnextnode;
738 struct interface *ifp;
739
740 /* scan all interfaces */
469351b3 741 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
742 struct listnode *chnode;
743 struct listnode *chnextnode;
744 struct pim_ifchannel *ch;
745 struct pim_interface *pim_ifp;
746
747 pim_ifp = ifp->info;
748 if (!pim_ifp)
749 continue;
750
751 /* search all ifchannels */
752 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
753 if (ch->upstream != up)
754 continue;
755
756 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
757 if (
758 /* RPF_interface(S) was NOT I */
759 (old_rpf_ifp == ch->interface)
760 &&
761 /* RPF_interface(S) stopped being I */
762 (ch->upstream->rpf.source_nexthop.interface != ch->interface)
763 ) {
764 assert_action_a5(ch);
765 }
766 } /* PIM_IFASSERT_I_AM_LOSER */
767
768 pim_ifchannel_update_assert_tracking_desired(ch);
769 }
770 }
771}
772
773void pim_upstream_update_could_assert(struct pim_upstream *up)
774{
775 struct listnode *ifnode;
776 struct listnode *ifnextnode;
777 struct listnode *chnode;
778 struct listnode *chnextnode;
779 struct interface *ifp;
780 struct pim_interface *pim_ifp;
781 struct pim_ifchannel *ch;
782
783 /* scan all interfaces */
469351b3 784 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
785 pim_ifp = ifp->info;
786 if (!pim_ifp)
787 continue;
788
789 /* scan per-interface (S,G) state */
790 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
791
792 if (ch->upstream != up)
793 continue;
794
795 pim_ifchannel_update_could_assert(ch);
796
797 } /* scan iface channel list */
798 } /* scan iflist */
799}
800
801void pim_upstream_update_my_assert_metric(struct pim_upstream *up)
802{
803 struct listnode *ifnode;
804 struct listnode *ifnextnode;
805 struct listnode *chnode;
806 struct listnode *chnextnode;
807 struct interface *ifp;
808 struct pim_interface *pim_ifp;
809 struct pim_ifchannel *ch;
810
811 /* scan all interfaces */
469351b3 812 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
813 pim_ifp = ifp->info;
814 if (!pim_ifp)
815 continue;
816
817 /* scan per-interface (S,G) state */
818 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
819
820 if (ch->upstream != up)
821 continue;
822
823 pim_ifchannel_update_my_assert_metric(ch);
824
825 } /* scan iface channel list */
826 } /* scan iflist */
827}
828
829static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up)
830{
831 struct listnode *ifnode;
832 struct listnode *ifnextnode;
833 struct listnode *chnode;
834 struct listnode *chnextnode;
835 struct interface *ifp;
836 struct pim_interface *pim_ifp;
837 struct pim_ifchannel *ch;
838
839 /* scan all interfaces */
469351b3 840 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
841 pim_ifp = ifp->info;
842 if (!pim_ifp)
843 continue;
844
845 /* scan per-interface (S,G) state */
846 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
847
848 if (ch->upstream != up)
849 continue;
850
851 pim_ifchannel_update_assert_tracking_desired(ch);
852
853 } /* scan iface channel list */
854 } /* scan iflist */
855}
f14248dd
DS
856
857/*
858 * On an RP, the PMBR value must be cleared when the
859 * Keepalive Timer expires
860 */
861static int
862pim_upstream_keep_alive_timer (struct thread *t)
863{
864 struct pim_upstream *up;
865
866 up = THREAD_ARG(t);
867
4ed0af70 868 if (I_am_RP (up->sg.grp))
25a335e0 869 {
65e1fcd7 870 pim_br_clear_pmbr (&up->sg);
25a335e0
DS
871 /*
872 * We need to do more here :)
873 * But this is the start.
874 */
875 }
14315ea8
DS
876
877 pim_mroute_update_counters (up->channel_oil);
878
bebc7290
DS
879 if (PIM_DEBUG_MROUTE)
880 {
881 zlog_debug ("New: %llu %lu %lu %lu", up->channel_oil->cc.lastused, up->channel_oil->cc.pktcnt,
882 up->channel_oil->cc.bytecnt, up->channel_oil->cc.wrong_if);
883 zlog_debug ("old: %llu %lu %lu %lu", up->channel_oil->cc.lastused, up->channel_oil->cc.oldpktcnt,
884 up->channel_oil->cc.oldbytecnt, up->channel_oil->cc.oldwrong_if);
885 }
e3be0432 886 if ((up->channel_oil->cc.oldpktcnt >= up->channel_oil->cc.pktcnt) &&
5f0336ed 887 (up->channel_oil->cc.lastused/100 >= qpim_keep_alive_time))
14315ea8
DS
888 {
889 pim_mroute_del (up->channel_oil);
d854589a
DS
890 THREAD_OFF (up->t_ka_timer);
891 THREAD_OFF (up->t_rs_timer);
892 THREAD_OFF (up->t_join_timer);
63c59d0c 893 pim_joinprune_send (up->rpf.source_nexthop.interface, up->rpf.rpf_addr.u.prefix4,
d854589a 894 &up->sg, 0);
a9b59879 895 PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM (up->flags);
5ce79466 896 if (PIM_UPSTREAM_FLAG_TEST_CREATED_BY_UPSTREAM(up->flags))
e5905a3b
DS
897 {
898 PIM_UPSTREAM_FLAG_UNSET_CREATED_BY_UPSTREAM(up->flags);
899 pim_upstream_del (up, __PRETTY_FUNCTION__);
900 }
14315ea8 901 }
25a335e0
DS
902 else
903 {
14315ea8 904 up->t_ka_timer = NULL;
4304f95c 905 pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
25a335e0 906 }
14315ea8 907
cb40b272 908 return 1;
f14248dd
DS
909}
910
f14248dd
DS
911void
912pim_upstream_keep_alive_timer_start (struct pim_upstream *up,
913 uint32_t time)
914{
00d2f9e4 915 THREAD_OFF (up->t_ka_timer);
f14248dd
DS
916 THREAD_TIMER_ON (master,
917 up->t_ka_timer,
918 pim_upstream_keep_alive_timer,
919 up, time);
920}
cb40b272
DS
921
922/*
923 * 4.2.1 Last-Hop Switchover to the SPT
924 *
925 * In Sparse-Mode PIM, last-hop routers join the shared tree towards the
926 * RP. Once traffic from sources to joined groups arrives at a last-hop
927 * router, it has the option of switching to receive the traffic on a
928 * shortest path tree (SPT).
929 *
930 * The decision for a router to switch to the SPT is controlled as
931 * follows:
932 *
933 * void
934 * CheckSwitchToSpt(S,G) {
935 * if ( ( pim_include(*,G) (-) pim_exclude(S,G)
936 * (+) pim_include(S,G) != NULL )
937 * AND SwitchToSptDesired(S,G) ) {
938 * # Note: Restarting the KAT will result in the SPT switch
939 * set KeepaliveTimer(S,G) to Keepalive_Period
940 * }
941 * }
942 *
943 * SwitchToSptDesired(S,G) is a policy function that is implementation
944 * defined. An "infinite threshold" policy can be implemented by making
945 * SwitchToSptDesired(S,G) return false all the time. A "switch on
946 * first packet" policy can be implemented by making
947 * SwitchToSptDesired(S,G) return true once a single packet has been
948 * received for the source and group.
949 */
950int
4ed0af70 951pim_upstream_switch_to_spt_desired (struct prefix_sg *sg)
cb40b272 952{
4ed0af70 953 if (I_am_RP (sg->grp))
a3b58b4a
DS
954 return 1;
955
cb40b272
DS
956 return 0;
957}
d7259eac
DS
958
959const char *
c9802954 960pim_upstream_state2str (enum pim_upstream_state join_state)
d7259eac 961{
c9802954 962 switch (join_state)
d7259eac
DS
963 {
964 case PIM_UPSTREAM_NOTJOINED:
e775c0a4 965 return "NotJoined";
d7259eac
DS
966 break;
967 case PIM_UPSTREAM_JOINED:
e775c0a4 968 return "Joined";
d7259eac
DS
969 break;
970 case PIM_UPSTREAM_JOIN_PENDING:
e775c0a4 971 return "JoinPending";
d7259eac
DS
972 break;
973 case PIM_UPSTREAM_PRUNE:
974 return "Prune";
975 break;
976 }
e775c0a4 977 return "Unknown";
d7259eac 978}
627ed2a3
DS
979
980static int
981pim_upstream_register_stop_timer (struct thread *t)
982{
4df01a4e 983 struct pim_interface *pim_ifp;
627ed2a3
DS
984 struct pim_upstream *up;
985 struct pim_rpf *rpg;
986 struct ip ip_hdr;
627ed2a3
DS
987 up = THREAD_ARG (t);
988
79ce47c0 989 THREAD_TIMER_OFF (up->t_rs_timer);
627ed2a3
DS
990 up->t_rs_timer = NULL;
991
992 if (PIM_DEBUG_TRACE)
993 {
d5ed8a9c
DS
994 zlog_debug ("%s: (S,G)=%s upstream register stop timer %s",
995 __PRETTY_FUNCTION__, pim_str_sg_dump (&up->sg),
c9802954 996 pim_upstream_state2str(up->join_state));
627ed2a3
DS
997 }
998
999 switch (up->join_state)
1000 {
1001 case PIM_UPSTREAM_JOIN_PENDING:
1002 up->join_state = PIM_UPSTREAM_JOINED;
bb027ee8
DS
1003 pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
1004 break;
1005 case PIM_UPSTREAM_JOINED:
627ed2a3
DS
1006 break;
1007 case PIM_UPSTREAM_PRUNE:
4df01a4e 1008 pim_ifp = up->rpf.source_nexthop.interface->info;
7ef66046
DS
1009 if (!pim_ifp)
1010 {
1011 if (PIM_DEBUG_TRACE)
1012 zlog_debug ("%s: Interface: %s is not configured for pim",
1013 __PRETTY_FUNCTION__, up->rpf.source_nexthop.interface->name);
1014 return 0;
1015 }
627ed2a3
DS
1016 up->join_state = PIM_UPSTREAM_JOIN_PENDING;
1017 pim_upstream_start_register_stop_timer (up, 1);
1018
4ed0af70 1019 rpg = RP (up->sg.grp);
627ed2a3
DS
1020 memset (&ip_hdr, 0, sizeof (struct ip));
1021 ip_hdr.ip_p = PIM_IP_PROTO_PIM;
1022 ip_hdr.ip_hl = 5;
1023 ip_hdr.ip_v = 4;
4ed0af70
DS
1024 ip_hdr.ip_src = up->sg.src;
1025 ip_hdr.ip_dst = up->sg.grp;
dc686f82 1026 ip_hdr.ip_len = htons (20);
627ed2a3 1027 // checksum is broken
4df01a4e
DS
1028 pim_register_send ((uint8_t *)&ip_hdr, sizeof (struct ip),
1029 pim_ifp->primary_address, rpg, 1);
627ed2a3
DS
1030 break;
1031 default:
1032 break;
1033 }
1034
1035 return 0;
1036}
1037
1038void
1039pim_upstream_start_register_stop_timer (struct pim_upstream *up, int null_register)
1040{
1041 uint32_t time;
1042
1043 if (up->t_rs_timer)
1044 {
1045 THREAD_TIMER_OFF (up->t_rs_timer);
1046 up->t_rs_timer = NULL;
1047 }
1048
1049 if (!null_register)
1050 {
1051 uint32_t lower = (0.5 * PIM_REGISTER_SUPPRESSION_PERIOD);
1052 uint32_t upper = (1.5 * PIM_REGISTER_SUPPRESSION_PERIOD);
1053 time = lower + (random () % (upper - lower + 1)) - PIM_REGISTER_PROBE_PERIOD;
1054 }
1055 else
1056 time = PIM_REGISTER_PROBE_PERIOD;
1057
1058 if (PIM_DEBUG_TRACE)
1059 {
05e451f8
DS
1060 zlog_debug ("%s: (S,G)=%s Starting upstream register stop timer %d",
1061 __PRETTY_FUNCTION__, pim_str_sg_dump (&up->sg), time);
627ed2a3
DS
1062 }
1063 THREAD_TIMER_ON (master, up->t_rs_timer,
1064 pim_upstream_register_stop_timer,
1065 up, time);
1066}
4fdc8f36
DS
1067
1068/*
1069 * For a given upstream, determine the inherited_olist
1070 * and apply it.
219e0013
DS
1071 *
1072 * inherited_olist(S,G,rpt) =
1073 * ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
1074 * (+) ( pim_include(*,G) (-) pim_exclude(S,G))
1075 * (-) ( lost_assert(*,G) (+) lost_assert(S,G,rpt) )
1076 *
1077 * inherited_olist(S,G) =
1078 * inherited_olist(S,G,rpt) (+)
1079 * joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
1080 *
4fdc8f36
DS
1081 * return 1 if there are any output interfaces
1082 * return 0 if there are not any output interfaces
1083 */
1084int
1085pim_upstream_inherited_olist (struct pim_upstream *up)
1086{
219e0013
DS
1087 struct pim_interface *pim_ifp;
1088 struct listnode *ifnextnode;
1089 struct listnode *chnextnode;
1090 struct pim_ifchannel *ch;
1091 struct listnode *chnode;
1092 struct listnode *ifnode;
1093 struct interface *ifp;
219e0013
DS
1094 int output_intf = 0;
1095
3667b0bc 1096 pim_ifp = up->rpf.source_nexthop.interface->info;
da55afba 1097 if (pim_ifp && !up->channel_oil)
3667b0bc 1098 up->channel_oil = pim_channel_oil_add (&up->sg, pim_ifp->mroute_vif_index);
219e0013 1099
7a3ddda5 1100 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp))
219e0013 1101 {
7a3ddda5
DS
1102 pim_ifp = ifp->info;
1103 if (!pim_ifp)
1104 continue;
219e0013 1105
7a3ddda5
DS
1106 for (ALL_LIST_ELEMENTS (pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch))
1107 {
1108 if (pim_upstream_evaluate_join_desired_interface (up, ch))
219e0013 1109 {
7a3ddda5
DS
1110 pim_channel_add_oif (up->channel_oil, ifp, PIM_OIF_FLAG_PROTO_PIM);
1111 output_intf++;
219e0013
DS
1112 }
1113 }
1114 }
1115
483eef9d
DS
1116 /*
1117 * If we have output_intf switch state to Join and work like normal
1118 * If we don't have an output_intf that means we are probably a
1119 * switch on a stick so turn on forwarding to just accept the
1120 * incoming packets so we don't bother the other stuff!
1121 */
1122 if (output_intf)
1123 pim_upstream_switch (up, PIM_UPSTREAM_JOINED);
1124 else
1125 forward_on (up);
219e0013
DS
1126
1127 return output_intf;
4fdc8f36 1128}
d3dd1804
DS
1129
1130/*
1131 * When we have a new neighbor,
1132 * find upstreams that don't have their rpf_addr
1133 * set and see if the new neighbor allows
1134 * the join to be sent
1135 */
1136void
1137pim_upstream_find_new_rpf (void)
1138{
1139 struct listnode *up_node;
1140 struct listnode *up_nextnode;
1141 struct pim_upstream *up;
1142
1143 /*
0f588989
DS
1144 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
1145 */
1146 for (ALL_LIST_ELEMENTS(pim_upstream_list, up_node, up_nextnode, up))
d3dd1804 1147 {
63c59d0c 1148 if (pim_rpf_addr_is_inaddr_any(&up->rpf))
d3dd1804
DS
1149 {
1150 if (PIM_DEBUG_PIM_TRACE)
1151 zlog_debug ("Upstream %s without a path to send join, checking",
1152 pim_str_sg_dump (&up->sg));
1153 pim_rpf_update (up, NULL);
1154 }
1155 }
1156}
0f588989
DS
1157
1158static int
1159pim_upstream_compare (const void *arg1, const void *arg2)
1160{
1161 const struct pim_upstream *up1 = (const struct pim_upstream *)arg1;
1162 const struct pim_upstream *up2 = (const struct pim_upstream *)arg2;
1163
1164 if (ntohl(up1->sg.grp.s_addr) < ntohl(up2->sg.grp.s_addr))
1165 return -1;
1166
1167 if (ntohl(up1->sg.grp.s_addr) > ntohl(up2->sg.grp.s_addr))
1168 return 1;
1169
1170 if (ntohl(up1->sg.src.s_addr) < ntohl(up2->sg.src.s_addr))
1171 return -1;
1172
1173 if (ntohl(up1->sg.src.s_addr) > ntohl(up2->sg.src.s_addr))
1174 return 1;
1175
1176 return 0;
1177}
1178
1179static unsigned int
1180pim_upstream_hash_key (void *arg)
1181{
1182 struct pim_upstream *up = (struct pim_upstream *)arg;
1183
1184 return jhash_2words (up->sg.src.s_addr, up->sg.grp.s_addr, 0);
1185}
1186
1187void pim_upstream_terminate (void)
1188{
1189 if (pim_upstream_list)
1190 list_free (pim_upstream_list);
1191 pim_upstream_list = NULL;
1192
1193 if (pim_upstream_hash)
1194 hash_free (pim_upstream_hash);
1195}
1196
1197static int
1198pim_upstream_equal (const void *arg1, const void *arg2)
1199{
1200 const struct pim_upstream *up1 = (const struct pim_upstream *)arg1;
1201 const struct pim_upstream *up2 = (const struct pim_upstream *)arg2;
1202
1203 if ((up1->sg.grp.s_addr == up2->sg.grp.s_addr) &&
1204 (up1->sg.src.s_addr == up2->sg.src.s_addr))
1205 return 1;
1206
1207 return 0;
1208}
1209
040d86ad
DS
1210void
1211pim_upstream_init (void)
0f588989
DS
1212{
1213 pim_upstream_hash = hash_create_size (8192, pim_upstream_hash_key,
1214 pim_upstream_equal);
1215
1216 pim_upstream_list = list_new ();
1217 pim_upstream_list->del = (void (*)(void *)) pim_upstream_free;
1218 pim_upstream_list->cmp = (int (*)(void *, void *)) pim_upstream_compare;
1219
1220}