]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_upstream.c
pim-msdp: part-1 - initial protocol infra.
[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,
372eab92 215 up,
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,
372eab92 484 up,
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 576 if (PIM_DEBUG_TRACE)
f4075cb4
DS
577 {
578 if (up)
579 zlog_debug("%s(%s): (%s), found: %d: ref_count: %d",
580 __PRETTY_FUNCTION__, name,
581 pim_str_sg_dump (&up->sg), found,
582 up->ref_count);
583 else
584 zlog_debug("%s(%s): (%s) failure to create",
585 __PRETTY_FUNCTION__, name,
586 pim_str_sg_dump (sg));
587 }
12e41d03 588
e5905a3b 589 return up;
12e41d03
DL
590}
591
7a3ddda5
DS
592static int
593pim_upstream_evaluate_join_desired_interface (struct pim_upstream *up,
594 struct pim_ifchannel *ch)
595{
596 struct pim_upstream *parent = up->parent;
597
598 if (ch->upstream == up)
599 {
600 if (!pim_macro_ch_lost_assert(ch) && pim_macro_chisin_joins_or_include(ch))
601 return 1;
602 }
603 /*
604 * joins (*,G)
605 */
606 if (parent && ch->upstream == parent)
607 {
608 if (!pim_macro_ch_lost_assert (ch) && pim_macro_chisin_joins_or_include (ch))
609 return 1;
610 }
611
612 return 0;
613}
614
12e41d03
DL
615/*
616 Evaluate JoinDesired(S,G):
617
618 JoinDesired(S,G) is true if there is a downstream (S,G) interface I
619 in the set:
620
621 inherited_olist(S,G) =
622 joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
623
624 JoinDesired(S,G) may be affected by changes in the following:
625
626 pim_ifp->primary_address
627 pim_ifp->pim_dr_addr
628 ch->ifassert_winner_metric
629 ch->ifassert_winner
630 ch->local_ifmembership
631 ch->ifjoin_state
632 ch->upstream->rpf.source_nexthop.mrib_metric_preference
633 ch->upstream->rpf.source_nexthop.mrib_route_metric
634 ch->upstream->rpf.source_nexthop.interface
635
636 See also pim_upstream_update_join_desired() below.
637 */
638int pim_upstream_evaluate_join_desired(struct pim_upstream *up)
639{
640 struct listnode *ifnode;
641 struct listnode *ifnextnode;
642 struct listnode *chnode;
643 struct listnode *chnextnode;
644 struct interface *ifp;
645 struct pim_interface *pim_ifp;
646 struct pim_ifchannel *ch;
7a3ddda5 647 int ret = 0;
12e41d03
DL
648
649 /* scan all interfaces */
469351b3 650 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
651 pim_ifp = ifp->info;
652 if (!pim_ifp)
653 continue;
654
655 /* scan per-interface (S,G) state */
7a3ddda5
DS
656 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch))
657 {
658 ret += pim_upstream_evaluate_join_desired_interface (up, ch);
659 } /* scan iface channel list */
12e41d03
DL
660 } /* scan iflist */
661
7a3ddda5 662 return ret; /* false */
12e41d03
DL
663}
664
665/*
666 See also pim_upstream_evaluate_join_desired() above.
667*/
668void pim_upstream_update_join_desired(struct pim_upstream *up)
669{
670 int was_join_desired; /* boolean */
671 int is_join_desired; /* boolean */
672
673 was_join_desired = PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags);
674
675 is_join_desired = pim_upstream_evaluate_join_desired(up);
676 if (is_join_desired)
677 PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up->flags);
678 else
679 PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up->flags);
680
681 /* switched from false to true */
682 if (is_join_desired && !was_join_desired) {
12e41d03
DL
683 pim_upstream_switch(up, PIM_UPSTREAM_JOINED);
684 return;
685 }
686
687 /* switched from true to false */
688 if (!is_join_desired && was_join_desired) {
12e41d03
DL
689 pim_upstream_switch(up, PIM_UPSTREAM_NOTJOINED);
690 return;
691 }
692}
693
694/*
695 RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
696 Transitions from Joined State
697 RPF'(S,G) GenID changes
698
699 The upstream (S,G) state machine remains in Joined state. If the
700 Join Timer is set to expire in more than t_override seconds, reset
701 it so that it expires after t_override seconds.
702*/
703void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr)
704{
705 struct listnode *up_node;
706 struct listnode *up_nextnode;
707 struct pim_upstream *up;
708
709 /*
0f588989
DS
710 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
711 */
712 for (ALL_LIST_ELEMENTS(pim_upstream_list, up_node, up_nextnode, up)) {
12e41d03
DL
713
714 if (PIM_DEBUG_PIM_TRACE) {
eaa54bdb
DW
715 char neigh_str[INET_ADDRSTRLEN];
716 char rpf_addr_str[PREFIX_STRLEN];
12e41d03 717 pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
63c59d0c 718 pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
05e451f8 719 zlog_debug("%s: matching neigh=%s against upstream (S,G)=%s joined=%d rpf_addr=%s",
12e41d03 720 __PRETTY_FUNCTION__,
05e451f8 721 neigh_str, pim_str_sg_dump (&up->sg),
12e41d03
DL
722 up->join_state == PIM_UPSTREAM_JOINED,
723 rpf_addr_str);
724 }
725
726 /* consider only (S,G) upstream in Joined state */
727 if (up->join_state != PIM_UPSTREAM_JOINED)
728 continue;
729
730 /* match RPF'(S,G)=neigh_addr */
63c59d0c 731 if (up->rpf.rpf_addr.u.prefix4.s_addr != neigh_addr.s_addr)
12e41d03
DL
732 continue;
733
734 pim_upstream_join_timer_decrease_to_t_override("RPF'(S,G) GenID change",
735 up, neigh_addr);
736 }
737}
738
739
740void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
741 struct interface *old_rpf_ifp)
742{
743 struct listnode *ifnode;
744 struct listnode *ifnextnode;
745 struct interface *ifp;
746
747 /* scan all interfaces */
469351b3 748 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
749 struct listnode *chnode;
750 struct listnode *chnextnode;
751 struct pim_ifchannel *ch;
752 struct pim_interface *pim_ifp;
753
754 pim_ifp = ifp->info;
755 if (!pim_ifp)
756 continue;
757
758 /* search all ifchannels */
759 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
760 if (ch->upstream != up)
761 continue;
762
763 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
764 if (
765 /* RPF_interface(S) was NOT I */
766 (old_rpf_ifp == ch->interface)
767 &&
768 /* RPF_interface(S) stopped being I */
769 (ch->upstream->rpf.source_nexthop.interface != ch->interface)
770 ) {
771 assert_action_a5(ch);
772 }
773 } /* PIM_IFASSERT_I_AM_LOSER */
774
775 pim_ifchannel_update_assert_tracking_desired(ch);
776 }
777 }
778}
779
780void pim_upstream_update_could_assert(struct pim_upstream *up)
781{
782 struct listnode *ifnode;
783 struct listnode *ifnextnode;
784 struct listnode *chnode;
785 struct listnode *chnextnode;
786 struct interface *ifp;
787 struct pim_interface *pim_ifp;
788 struct pim_ifchannel *ch;
789
790 /* scan all interfaces */
469351b3 791 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
792 pim_ifp = ifp->info;
793 if (!pim_ifp)
794 continue;
795
796 /* scan per-interface (S,G) state */
797 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
798
799 if (ch->upstream != up)
800 continue;
801
802 pim_ifchannel_update_could_assert(ch);
803
804 } /* scan iface channel list */
805 } /* scan iflist */
806}
807
808void pim_upstream_update_my_assert_metric(struct pim_upstream *up)
809{
810 struct listnode *ifnode;
811 struct listnode *ifnextnode;
812 struct listnode *chnode;
813 struct listnode *chnextnode;
814 struct interface *ifp;
815 struct pim_interface *pim_ifp;
816 struct pim_ifchannel *ch;
817
818 /* scan all interfaces */
469351b3 819 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
820 pim_ifp = ifp->info;
821 if (!pim_ifp)
822 continue;
823
824 /* scan per-interface (S,G) state */
825 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
826
827 if (ch->upstream != up)
828 continue;
829
830 pim_ifchannel_update_my_assert_metric(ch);
831
832 } /* scan iface channel list */
833 } /* scan iflist */
834}
835
836static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up)
837{
838 struct listnode *ifnode;
839 struct listnode *ifnextnode;
840 struct listnode *chnode;
841 struct listnode *chnextnode;
842 struct interface *ifp;
843 struct pim_interface *pim_ifp;
844 struct pim_ifchannel *ch;
845
846 /* scan all interfaces */
469351b3 847 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
848 pim_ifp = ifp->info;
849 if (!pim_ifp)
850 continue;
851
852 /* scan per-interface (S,G) state */
853 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
854
855 if (ch->upstream != up)
856 continue;
857
858 pim_ifchannel_update_assert_tracking_desired(ch);
859
860 } /* scan iface channel list */
861 } /* scan iflist */
862}
f14248dd
DS
863
864/*
865 * On an RP, the PMBR value must be cleared when the
866 * Keepalive Timer expires
867 */
868static int
869pim_upstream_keep_alive_timer (struct thread *t)
870{
871 struct pim_upstream *up;
872
873 up = THREAD_ARG(t);
874
4ed0af70 875 if (I_am_RP (up->sg.grp))
25a335e0 876 {
65e1fcd7 877 pim_br_clear_pmbr (&up->sg);
25a335e0
DS
878 /*
879 * We need to do more here :)
880 * But this is the start.
881 */
882 }
14315ea8
DS
883
884 pim_mroute_update_counters (up->channel_oil);
885
bebc7290
DS
886 if (PIM_DEBUG_MROUTE)
887 {
888 zlog_debug ("New: %llu %lu %lu %lu", up->channel_oil->cc.lastused, up->channel_oil->cc.pktcnt,
889 up->channel_oil->cc.bytecnt, up->channel_oil->cc.wrong_if);
890 zlog_debug ("old: %llu %lu %lu %lu", up->channel_oil->cc.lastused, up->channel_oil->cc.oldpktcnt,
891 up->channel_oil->cc.oldbytecnt, up->channel_oil->cc.oldwrong_if);
892 }
e3be0432 893 if ((up->channel_oil->cc.oldpktcnt >= up->channel_oil->cc.pktcnt) &&
5f0336ed 894 (up->channel_oil->cc.lastused/100 >= qpim_keep_alive_time))
14315ea8
DS
895 {
896 pim_mroute_del (up->channel_oil);
d854589a
DS
897 THREAD_OFF (up->t_ka_timer);
898 THREAD_OFF (up->t_rs_timer);
899 THREAD_OFF (up->t_join_timer);
63c59d0c 900 pim_joinprune_send (up->rpf.source_nexthop.interface, up->rpf.rpf_addr.u.prefix4,
372eab92 901 up, 0);
a9b59879 902 PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM (up->flags);
5ce79466 903 if (PIM_UPSTREAM_FLAG_TEST_CREATED_BY_UPSTREAM(up->flags))
e5905a3b
DS
904 {
905 PIM_UPSTREAM_FLAG_UNSET_CREATED_BY_UPSTREAM(up->flags);
906 pim_upstream_del (up, __PRETTY_FUNCTION__);
907 }
14315ea8 908 }
25a335e0
DS
909 else
910 {
14315ea8 911 up->t_ka_timer = NULL;
4304f95c 912 pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
25a335e0 913 }
14315ea8 914
cb40b272 915 return 1;
f14248dd
DS
916}
917
f14248dd
DS
918void
919pim_upstream_keep_alive_timer_start (struct pim_upstream *up,
920 uint32_t time)
921{
00d2f9e4 922 THREAD_OFF (up->t_ka_timer);
f14248dd
DS
923 THREAD_TIMER_ON (master,
924 up->t_ka_timer,
925 pim_upstream_keep_alive_timer,
926 up, time);
927}
cb40b272
DS
928
929/*
930 * 4.2.1 Last-Hop Switchover to the SPT
931 *
932 * In Sparse-Mode PIM, last-hop routers join the shared tree towards the
933 * RP. Once traffic from sources to joined groups arrives at a last-hop
934 * router, it has the option of switching to receive the traffic on a
935 * shortest path tree (SPT).
936 *
937 * The decision for a router to switch to the SPT is controlled as
938 * follows:
939 *
940 * void
941 * CheckSwitchToSpt(S,G) {
942 * if ( ( pim_include(*,G) (-) pim_exclude(S,G)
943 * (+) pim_include(S,G) != NULL )
944 * AND SwitchToSptDesired(S,G) ) {
945 * # Note: Restarting the KAT will result in the SPT switch
946 * set KeepaliveTimer(S,G) to Keepalive_Period
947 * }
948 * }
949 *
950 * SwitchToSptDesired(S,G) is a policy function that is implementation
951 * defined. An "infinite threshold" policy can be implemented by making
952 * SwitchToSptDesired(S,G) return false all the time. A "switch on
953 * first packet" policy can be implemented by making
954 * SwitchToSptDesired(S,G) return true once a single packet has been
955 * received for the source and group.
956 */
957int
4ed0af70 958pim_upstream_switch_to_spt_desired (struct prefix_sg *sg)
cb40b272 959{
4ed0af70 960 if (I_am_RP (sg->grp))
a3b58b4a
DS
961 return 1;
962
cb40b272
DS
963 return 0;
964}
d7259eac
DS
965
966const char *
c9802954 967pim_upstream_state2str (enum pim_upstream_state join_state)
d7259eac 968{
c9802954 969 switch (join_state)
d7259eac
DS
970 {
971 case PIM_UPSTREAM_NOTJOINED:
e775c0a4 972 return "NotJoined";
d7259eac
DS
973 break;
974 case PIM_UPSTREAM_JOINED:
e775c0a4 975 return "Joined";
d7259eac
DS
976 break;
977 case PIM_UPSTREAM_JOIN_PENDING:
e775c0a4 978 return "JoinPending";
d7259eac
DS
979 break;
980 case PIM_UPSTREAM_PRUNE:
981 return "Prune";
982 break;
983 }
e775c0a4 984 return "Unknown";
d7259eac 985}
627ed2a3
DS
986
987static int
988pim_upstream_register_stop_timer (struct thread *t)
989{
4df01a4e 990 struct pim_interface *pim_ifp;
627ed2a3
DS
991 struct pim_upstream *up;
992 struct pim_rpf *rpg;
993 struct ip ip_hdr;
627ed2a3
DS
994 up = THREAD_ARG (t);
995
79ce47c0 996 THREAD_TIMER_OFF (up->t_rs_timer);
627ed2a3
DS
997 up->t_rs_timer = NULL;
998
999 if (PIM_DEBUG_TRACE)
1000 {
d5ed8a9c
DS
1001 zlog_debug ("%s: (S,G)=%s upstream register stop timer %s",
1002 __PRETTY_FUNCTION__, pim_str_sg_dump (&up->sg),
c9802954 1003 pim_upstream_state2str(up->join_state));
627ed2a3
DS
1004 }
1005
1006 switch (up->join_state)
1007 {
1008 case PIM_UPSTREAM_JOIN_PENDING:
1009 up->join_state = PIM_UPSTREAM_JOINED;
bb027ee8
DS
1010 pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
1011 break;
1012 case PIM_UPSTREAM_JOINED:
627ed2a3
DS
1013 break;
1014 case PIM_UPSTREAM_PRUNE:
4df01a4e 1015 pim_ifp = up->rpf.source_nexthop.interface->info;
7ef66046
DS
1016 if (!pim_ifp)
1017 {
1018 if (PIM_DEBUG_TRACE)
1019 zlog_debug ("%s: Interface: %s is not configured for pim",
1020 __PRETTY_FUNCTION__, up->rpf.source_nexthop.interface->name);
1021 return 0;
1022 }
627ed2a3
DS
1023 up->join_state = PIM_UPSTREAM_JOIN_PENDING;
1024 pim_upstream_start_register_stop_timer (up, 1);
1025
4ed0af70 1026 rpg = RP (up->sg.grp);
627ed2a3
DS
1027 memset (&ip_hdr, 0, sizeof (struct ip));
1028 ip_hdr.ip_p = PIM_IP_PROTO_PIM;
1029 ip_hdr.ip_hl = 5;
1030 ip_hdr.ip_v = 4;
4ed0af70
DS
1031 ip_hdr.ip_src = up->sg.src;
1032 ip_hdr.ip_dst = up->sg.grp;
dc686f82 1033 ip_hdr.ip_len = htons (20);
627ed2a3 1034 // checksum is broken
4df01a4e
DS
1035 pim_register_send ((uint8_t *)&ip_hdr, sizeof (struct ip),
1036 pim_ifp->primary_address, rpg, 1);
627ed2a3
DS
1037 break;
1038 default:
1039 break;
1040 }
1041
1042 return 0;
1043}
1044
1045void
1046pim_upstream_start_register_stop_timer (struct pim_upstream *up, int null_register)
1047{
1048 uint32_t time;
1049
1050 if (up->t_rs_timer)
1051 {
1052 THREAD_TIMER_OFF (up->t_rs_timer);
1053 up->t_rs_timer = NULL;
1054 }
1055
1056 if (!null_register)
1057 {
1058 uint32_t lower = (0.5 * PIM_REGISTER_SUPPRESSION_PERIOD);
1059 uint32_t upper = (1.5 * PIM_REGISTER_SUPPRESSION_PERIOD);
1060 time = lower + (random () % (upper - lower + 1)) - PIM_REGISTER_PROBE_PERIOD;
1061 }
1062 else
1063 time = PIM_REGISTER_PROBE_PERIOD;
1064
1065 if (PIM_DEBUG_TRACE)
1066 {
05e451f8
DS
1067 zlog_debug ("%s: (S,G)=%s Starting upstream register stop timer %d",
1068 __PRETTY_FUNCTION__, pim_str_sg_dump (&up->sg), time);
627ed2a3
DS
1069 }
1070 THREAD_TIMER_ON (master, up->t_rs_timer,
1071 pim_upstream_register_stop_timer,
1072 up, time);
1073}
4fdc8f36
DS
1074
1075/*
1076 * For a given upstream, determine the inherited_olist
1077 * and apply it.
219e0013
DS
1078 *
1079 * inherited_olist(S,G,rpt) =
1080 * ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
1081 * (+) ( pim_include(*,G) (-) pim_exclude(S,G))
1082 * (-) ( lost_assert(*,G) (+) lost_assert(S,G,rpt) )
1083 *
1084 * inherited_olist(S,G) =
1085 * inherited_olist(S,G,rpt) (+)
1086 * joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
1087 *
4fdc8f36
DS
1088 * return 1 if there are any output interfaces
1089 * return 0 if there are not any output interfaces
1090 */
1091int
1092pim_upstream_inherited_olist (struct pim_upstream *up)
1093{
219e0013
DS
1094 struct pim_interface *pim_ifp;
1095 struct listnode *ifnextnode;
1096 struct listnode *chnextnode;
1097 struct pim_ifchannel *ch;
1098 struct listnode *chnode;
1099 struct listnode *ifnode;
1100 struct interface *ifp;
219e0013
DS
1101 int output_intf = 0;
1102
3667b0bc 1103 pim_ifp = up->rpf.source_nexthop.interface->info;
da55afba 1104 if (pim_ifp && !up->channel_oil)
3667b0bc 1105 up->channel_oil = pim_channel_oil_add (&up->sg, pim_ifp->mroute_vif_index);
219e0013 1106
7a3ddda5 1107 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp))
219e0013 1108 {
7a3ddda5
DS
1109 pim_ifp = ifp->info;
1110 if (!pim_ifp)
1111 continue;
219e0013 1112
7a3ddda5
DS
1113 for (ALL_LIST_ELEMENTS (pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch))
1114 {
1115 if (pim_upstream_evaluate_join_desired_interface (up, ch))
219e0013 1116 {
7a3ddda5
DS
1117 pim_channel_add_oif (up->channel_oil, ifp, PIM_OIF_FLAG_PROTO_PIM);
1118 output_intf++;
219e0013
DS
1119 }
1120 }
1121 }
1122
483eef9d
DS
1123 /*
1124 * If we have output_intf switch state to Join and work like normal
1125 * If we don't have an output_intf that means we are probably a
1126 * switch on a stick so turn on forwarding to just accept the
1127 * incoming packets so we don't bother the other stuff!
1128 */
1129 if (output_intf)
1130 pim_upstream_switch (up, PIM_UPSTREAM_JOINED);
1131 else
1132 forward_on (up);
219e0013
DS
1133
1134 return output_intf;
4fdc8f36 1135}
d3dd1804
DS
1136
1137/*
1138 * When we have a new neighbor,
1139 * find upstreams that don't have their rpf_addr
1140 * set and see if the new neighbor allows
1141 * the join to be sent
1142 */
1143void
1144pim_upstream_find_new_rpf (void)
1145{
1146 struct listnode *up_node;
1147 struct listnode *up_nextnode;
1148 struct pim_upstream *up;
1149
1150 /*
0f588989
DS
1151 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
1152 */
1153 for (ALL_LIST_ELEMENTS(pim_upstream_list, up_node, up_nextnode, up))
d3dd1804 1154 {
63c59d0c 1155 if (pim_rpf_addr_is_inaddr_any(&up->rpf))
d3dd1804
DS
1156 {
1157 if (PIM_DEBUG_PIM_TRACE)
1158 zlog_debug ("Upstream %s without a path to send join, checking",
1159 pim_str_sg_dump (&up->sg));
1160 pim_rpf_update (up, NULL);
1161 }
1162 }
1163}
0f588989
DS
1164
1165static int
1166pim_upstream_compare (const void *arg1, const void *arg2)
1167{
1168 const struct pim_upstream *up1 = (const struct pim_upstream *)arg1;
1169 const struct pim_upstream *up2 = (const struct pim_upstream *)arg2;
1170
1171 if (ntohl(up1->sg.grp.s_addr) < ntohl(up2->sg.grp.s_addr))
1172 return -1;
1173
1174 if (ntohl(up1->sg.grp.s_addr) > ntohl(up2->sg.grp.s_addr))
1175 return 1;
1176
1177 if (ntohl(up1->sg.src.s_addr) < ntohl(up2->sg.src.s_addr))
1178 return -1;
1179
1180 if (ntohl(up1->sg.src.s_addr) > ntohl(up2->sg.src.s_addr))
1181 return 1;
1182
1183 return 0;
1184}
1185
1186static unsigned int
1187pim_upstream_hash_key (void *arg)
1188{
1189 struct pim_upstream *up = (struct pim_upstream *)arg;
1190
1191 return jhash_2words (up->sg.src.s_addr, up->sg.grp.s_addr, 0);
1192}
1193
1194void pim_upstream_terminate (void)
1195{
1196 if (pim_upstream_list)
1197 list_free (pim_upstream_list);
1198 pim_upstream_list = NULL;
1199
1200 if (pim_upstream_hash)
1201 hash_free (pim_upstream_hash);
1202}
1203
1204static int
1205pim_upstream_equal (const void *arg1, const void *arg2)
1206{
1207 const struct pim_upstream *up1 = (const struct pim_upstream *)arg1;
1208 const struct pim_upstream *up2 = (const struct pim_upstream *)arg2;
1209
1210 if ((up1->sg.grp.s_addr == up2->sg.grp.s_addr) &&
1211 (up1->sg.src.s_addr == up2->sg.src.s_addr))
1212 return 1;
1213
1214 return 0;
1215}
1216
040d86ad
DS
1217void
1218pim_upstream_init (void)
0f588989
DS
1219{
1220 pim_upstream_hash = hash_create_size (8192, pim_upstream_hash_key,
1221 pim_upstream_equal);
1222
1223 pim_upstream_list = list_new ();
1224 pim_upstream_list->del = (void (*)(void *)) pim_upstream_free;
1225 pim_upstream_list->cmp = (int (*)(void *, void *)) pim_upstream_compare;
1226
1227}