blob: b9cf1e52cdeb72acab989a876a8d08e5f39e07a6 [file] [log] [blame]
Everton Marques871dbcf2009-08-11 15:43:05 -03001/*
2 PIM for Quagga
3 Copyright (C) 2008 Everton da Silva Marques
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
18 MA 02110-1301 USA
19
20 $QuaggaId: $Format:%an, %ai, %h$ $
21*/
22
23#include <zebra.h>
24
25#include "zebra/rib.h"
26
27#include "log.h"
28#include "zclient.h"
29#include "memory.h"
30#include "thread.h"
31#include "linklist.h"
32
33#include "pimd.h"
34#include "pim_pim.h"
35#include "pim_str.h"
36#include "pim_time.h"
37#include "pim_iface.h"
38#include "pim_join.h"
39#include "pim_zlookup.h"
40#include "pim_upstream.h"
41#include "pim_ifchannel.h"
42#include "pim_neighbor.h"
43#include "pim_rpf.h"
44#include "pim_zebra.h"
45#include "pim_oil.h"
46#include "pim_macro.h"
47
48static void join_timer_start(struct pim_upstream *up);
49static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up);
50
51void pim_upstream_free(struct pim_upstream *up)
52{
53 XFREE(MTYPE_PIM_UPSTREAM, up);
54}
55
56static void upstream_channel_oil_detach(struct pim_upstream *up)
57{
58 if (up->channel_oil) {
59 pim_channel_oil_del(up->channel_oil);
60 up->channel_oil = 0;
61 }
62}
63
64void pim_upstream_delete(struct pim_upstream *up)
65{
66 THREAD_OFF(up->t_join_timer);
67
68 upstream_channel_oil_detach(up);
69
70 /*
71 notice that listnode_delete() can't be moved
72 into pim_upstream_free() because the later is
73 called by list_delete_all_node()
74 */
75 listnode_delete(qpim_upstream_list, up);
76
77 pim_upstream_free(up);
78}
79
80static void send_join(struct pim_upstream *up)
81{
82 zassert(up->join_state == PIM_UPSTREAM_JOINED);
83
84
85 if (PIM_DEBUG_PIM_TRACE) {
86 if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
87 char src_str[100];
88 char grp_str[100];
89 char rpf_str[100];
90 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
91 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
92 pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
93 zlog_warn("%s: can't send join upstream: RPF'(%s,%s)=%s",
94 __PRETTY_FUNCTION__,
95 src_str, grp_str, rpf_str);
96 /* warning only */
97 }
98 }
99
100 /* send Join(S,G) to the current upstream neighbor */
101 pim_joinprune_send(up->rpf.source_nexthop.interface,
102 up->rpf.rpf_addr,
103 up->source_addr,
104 up->group_addr,
105 1 /* join */);
106}
107
108static int on_join_timer(struct thread *t)
109{
110 struct pim_upstream *up;
111
112 zassert(t);
113 up = THREAD_ARG(t);
114 zassert(up);
115
116 send_join(up);
117
118 up->t_join_timer = 0;
119 join_timer_start(up);
120
121 return 0;
122}
123
124static void join_timer_start(struct pim_upstream *up)
125{
126 if (PIM_DEBUG_PIM_EVENTS) {
127 char src_str[100];
128 char grp_str[100];
129 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
130 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
131 zlog_debug("%s: starting %d sec timer for upstream (S,G)=(%s,%s)",
132 __PRETTY_FUNCTION__,
133 qpim_t_periodic,
134 src_str, grp_str);
135 }
136
137 zassert(!up->t_join_timer);
138
139 THREAD_TIMER_ON(master, up->t_join_timer,
140 on_join_timer,
141 up, qpim_t_periodic);
142}
143
144void pim_upstream_join_timer_restart(struct pim_upstream *up)
145{
146 THREAD_OFF(up->t_join_timer);
147 join_timer_start(up);
148}
149
150static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up,
151 int interval_msec)
152{
153 if (PIM_DEBUG_PIM_EVENTS) {
154 char src_str[100];
155 char grp_str[100];
156 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
157 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
158 zlog_debug("%s: restarting %d msec timer for upstream (S,G)=(%s,%s)",
159 __PRETTY_FUNCTION__,
160 interval_msec,
161 src_str, grp_str);
162 }
163
164 THREAD_OFF(up->t_join_timer);
165 THREAD_TIMER_MSEC_ON(master, up->t_join_timer,
166 on_join_timer,
167 up, interval_msec);
168}
169
170void pim_upstream_join_suppress(struct pim_upstream *up,
171 struct in_addr rpf_addr,
172 int holdtime)
173{
174 long t_joinsuppress_msec;
175 long join_timer_remain_msec;
176
177 t_joinsuppress_msec = MIN(pim_if_t_suppressed_msec(up->rpf.source_nexthop.interface),
178 1000 * holdtime);
179
180 join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
181
182 if (PIM_DEBUG_PIM_TRACE) {
183 char src_str[100];
184 char grp_str[100];
185 char rpf_str[100];
186 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
187 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
188 pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
189 zlog_debug("%s %s: detected Join(%s,%s) to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
190 __FILE__, __PRETTY_FUNCTION__,
191 src_str, grp_str,
192 rpf_str,
193 join_timer_remain_msec, t_joinsuppress_msec);
194 }
195
196 if (join_timer_remain_msec < t_joinsuppress_msec) {
197 if (PIM_DEBUG_PIM_TRACE) {
198 char src_str[100];
199 char grp_str[100];
200 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
201 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
202 zlog_debug("%s %s: suppressing Join(S,G)=(%s,%s) for %ld msec",
203 __FILE__, __PRETTY_FUNCTION__,
204 src_str, grp_str, t_joinsuppress_msec);
205 }
206
207 pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec);
208 }
209}
210
211void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
212 struct pim_upstream *up,
213 struct in_addr rpf_addr)
214{
215 long join_timer_remain_msec;
216 int t_override_msec;
217
218 join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
219 t_override_msec = pim_if_t_override_msec(up->rpf.source_nexthop.interface);
220
221 if (PIM_DEBUG_PIM_TRACE) {
222 char src_str[100];
223 char grp_str[100];
224 char rpf_str[100];
225 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
226 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
227 pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
228 zlog_debug("%s: to RPF'(%s,%s)=%s: join_timer=%ld msec t_override=%d msec",
229 debug_label,
230 src_str, grp_str, rpf_str,
231 join_timer_remain_msec, t_override_msec);
232 }
233
234 if (join_timer_remain_msec > t_override_msec) {
235 if (PIM_DEBUG_PIM_TRACE) {
236 char src_str[100];
237 char grp_str[100];
238 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
239 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
240 zlog_debug("%s: decreasing (S,G)=(%s,%s) join timer to t_override=%d msec",
241 debug_label,
242 src_str, grp_str,
243 t_override_msec);
244 }
245
246 pim_upstream_join_timer_restart_msec(up, t_override_msec);
247 }
248}
249
250static void forward_on(struct pim_upstream *up)
251{
252 struct listnode *ifnode;
253 struct listnode *ifnextnode;
254 struct listnode *chnode;
255 struct listnode *chnextnode;
256 struct interface *ifp;
257 struct pim_interface *pim_ifp;
258 struct pim_ifchannel *ch;
259 struct in_addr ifaddr;
260
261 /* scan all interfaces */
262 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
263 pim_ifp = ifp->info;
264 if (!pim_ifp)
265 continue;
266
267 ifaddr = pim_ifp->primary_address;
268
269 /* scan per-interface (S,G) state */
270 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
271
272 if (ch->upstream != up)
273 continue;
274
275 if (pim_macro_chisin_oiflist(ch))
276 pim_forward_start(ch);
277
278 } /* scan iface channel list */
279 } /* scan iflist */
280}
281
282static void forward_off(struct pim_upstream *up)
283{
284 struct listnode *ifnode;
285 struct listnode *ifnextnode;
286 struct listnode *chnode;
287 struct listnode *chnextnode;
288 struct interface *ifp;
289 struct pim_interface *pim_ifp;
290 struct pim_ifchannel *ch;
291
292 /* scan all interfaces */
293 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
294 pim_ifp = ifp->info;
295 if (!pim_ifp)
296 continue;
297
298 /* scan per-interface (S,G) state */
299 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
300
301 if (ch->upstream != up)
302 continue;
303
304 pim_forward_stop(ch);
305
306 } /* scan iface channel list */
307 } /* scan iflist */
308}
309
310static void pim_upstream_switch(struct pim_upstream *up,
311 enum pim_upstream_state new_state)
312{
313 enum pim_upstream_state old_state = up->join_state;
314
315 zassert(old_state != new_state);
316
317 up->join_state = new_state;
318 up->state_transition = pim_time_monotonic_sec();
319
320 if (PIM_DEBUG_PIM_EVENTS) {
321 char src_str[100];
322 char grp_str[100];
323 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
324 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
325 zlog_debug("%s: PIM_UPSTREAM_%s: (S,G)=(%s,%s)",
326 __PRETTY_FUNCTION__,
327 ((new_state == PIM_UPSTREAM_JOINED) ? "JOINED" : "NOTJOINED"),
328 src_str, grp_str);
329 }
330
331 pim_upstream_update_assert_tracking_desired(up);
332
333 if (new_state == PIM_UPSTREAM_JOINED) {
334 forward_on(up);
335 send_join(up);
336 join_timer_start(up);
337 }
338 else {
339 forward_off(up);
340 pim_joinprune_send(up->rpf.source_nexthop.interface,
341 up->rpf.rpf_addr,
342 up->source_addr,
343 up->group_addr,
344 0 /* prune */);
345 zassert(up->t_join_timer);
346 THREAD_OFF(up->t_join_timer);
347 }
348
349}
350
351static struct pim_upstream *pim_upstream_new(struct in_addr source_addr,
352 struct in_addr group_addr)
353{
354 struct pim_upstream *up;
355
356 up = XMALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
357 if (!up) {
358 zlog_err("%s: PIM XMALLOC(%d) failure",
359 __PRETTY_FUNCTION__, sizeof(*up));
360 return 0;
361 }
362
363 up->source_addr = source_addr;
364 up->group_addr = group_addr;
365 up->flags = 0;
366 up->ref_count = 1;
367 up->t_join_timer = 0;
368 up->join_state = 0;
369 up->state_transition = pim_time_monotonic_sec();
370 up->channel_oil = 0;
371
372 up->rpf.source_nexthop.interface = 0;
373 up->rpf.source_nexthop.mrib_nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
374 up->rpf.source_nexthop.mrib_metric_preference = qpim_infinite_assert_metric.metric_preference;
375 up->rpf.source_nexthop.mrib_route_metric = qpim_infinite_assert_metric.route_metric;
376 up->rpf.rpf_addr.s_addr = PIM_NET_INADDR_ANY;
377
378 pim_rpf_update(up, 0);
379
380 listnode_add(qpim_upstream_list, up);
381
382 return up;
383}
384
385struct pim_upstream *pim_upstream_find(struct in_addr source_addr,
386 struct in_addr group_addr)
387{
388 struct listnode *up_node;
389 struct pim_upstream *up;
390
391 for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) {
392 if (
393 (source_addr.s_addr == up->source_addr.s_addr) &&
394 (group_addr.s_addr == up->group_addr.s_addr)
395 ) {
396 return up;
397 }
398 }
399
400 return 0;
401}
402
403struct pim_upstream *pim_upstream_add(struct in_addr source_addr,
404 struct in_addr group_addr)
405{
406 struct pim_upstream *up;
407
408 up = pim_upstream_find(source_addr, group_addr);
409 if (up) {
410 ++up->ref_count;
411 }
412 else {
413 up = pim_upstream_new(source_addr, group_addr);
414 }
415
416 return up;
417}
418
419void pim_upstream_del(struct pim_upstream *up)
420{
421 --up->ref_count;
422
423 if (up->ref_count < 1) {
424 pim_upstream_delete(up);
425 }
426}
427
428/*
429 Evaluate JoinDesired(S,G):
430
431 JoinDesired(S,G) is true if there is a downstream (S,G) interface I
432 in the set:
433
434 inherited_olist(S,G) =
435 joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
436
437 JoinDesired(S,G) may be affected by changes in the following:
438
439 pim_ifp->primary_address
440 pim_ifp->pim_dr_addr
441 ch->ifassert_winner_metric
442 ch->ifassert_winner
443 ch->local_ifmembership
444 ch->ifjoin_state
445 ch->upstream->rpf.source_nexthop.mrib_metric_preference
446 ch->upstream->rpf.source_nexthop.mrib_route_metric
447 ch->upstream->rpf.source_nexthop.interface
448
449 See also pim_upstream_update_join_desired() below.
450 */
451int pim_upstream_evaluate_join_desired(struct pim_upstream *up)
452{
453 struct listnode *ifnode;
454 struct listnode *ifnextnode;
455 struct listnode *chnode;
456 struct listnode *chnextnode;
457 struct interface *ifp;
458 struct pim_interface *pim_ifp;
459 struct pim_ifchannel *ch;
460
461 /* scan all interfaces */
462 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
463 pim_ifp = ifp->info;
464 if (!pim_ifp)
465 continue;
466
467 /* scan per-interface (S,G) state */
468 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
469 if (ch->upstream != up)
470 continue;
471
472 if (pim_macro_ch_lost_assert(ch))
473 continue; /* keep searching */
474
475 if (pim_macro_chisin_joins_or_include(ch))
476 return 1; /* true */
477 } /* scan iface channel list */
478 } /* scan iflist */
479
480 return 0; /* false */
481}
482
483/*
484 See also pim_upstream_evaluate_join_desired() above.
485*/
486void pim_upstream_update_join_desired(struct pim_upstream *up)
487{
488 int was_join_desired; /* boolean */
489 int is_join_desired; /* boolean */
490
491 was_join_desired = PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags);
492
493 is_join_desired = pim_upstream_evaluate_join_desired(up);
494 if (is_join_desired)
495 PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up->flags);
496 else
497 PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up->flags);
498
499 /* switched from false to true */
500 if (is_join_desired && !was_join_desired) {
501 zassert(up->join_state == PIM_UPSTREAM_NOTJOINED);
502 pim_upstream_switch(up, PIM_UPSTREAM_JOINED);
503 return;
504 }
505
506 /* switched from true to false */
507 if (!is_join_desired && was_join_desired) {
508 zassert(up->join_state == PIM_UPSTREAM_JOINED);
509 pim_upstream_switch(up, PIM_UPSTREAM_NOTJOINED);
510 return;
511 }
512}
513
514/*
515 RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
516 Transitions from Joined State
517 RPF'(S,G) GenID changes
518
519 The upstream (S,G) state machine remains in Joined state. If the
520 Join Timer is set to expire in more than t_override seconds, reset
521 it so that it expires after t_override seconds.
522*/
523void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr)
524{
525 struct listnode *up_node;
526 struct listnode *up_nextnode;
527 struct pim_upstream *up;
528
529 /*
530 Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
531 */
532 for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) {
533
534 if (PIM_DEBUG_PIM_TRACE) {
535 char neigh_str[100];
536 char src_str[100];
537 char grp_str[100];
538 char rpf_addr_str[100];
539 pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
540 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
541 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
542 pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
543 zlog_debug("%s: matching neigh=%s against upstream (S,G)=(%s,%s) joined=%d rpf_addr=%s",
544 __PRETTY_FUNCTION__,
545 neigh_str, src_str, grp_str,
546 up->join_state == PIM_UPSTREAM_JOINED,
547 rpf_addr_str);
548 }
549
550 /* consider only (S,G) upstream in Joined state */
551 if (up->join_state != PIM_UPSTREAM_JOINED)
552 continue;
553
554 /* match RPF'(S,G)=neigh_addr */
555 if (up->rpf.rpf_addr.s_addr != neigh_addr.s_addr)
556 continue;
557
558 pim_upstream_join_timer_decrease_to_t_override("RPF'(S,G) GenID change",
559 up, neigh_addr);
560 }
561}
562
563
564void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
565 struct interface *old_rpf_ifp)
566{
567 struct listnode *ifnode;
568 struct listnode *ifnextnode;
569 struct interface *ifp;
570
571 /* scan all interfaces */
572 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
573 struct listnode *chnode;
574 struct listnode *chnextnode;
575 struct pim_ifchannel *ch;
576 struct pim_interface *pim_ifp;
577
578 pim_ifp = ifp->info;
579 if (!pim_ifp)
580 continue;
581
582 /* search all ifchannels */
583 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
584 if (ch->upstream != up)
585 continue;
586
587 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
588 if (
589 /* RPF_interface(S) was NOT I */
590 (old_rpf_ifp == ch->interface)
591 &&
592 /* RPF_interface(S) stopped being I */
593 (ch->upstream->rpf.source_nexthop.interface != ch->interface)
594 ) {
595 assert_action_a5(ch);
596 }
597 } /* PIM_IFASSERT_I_AM_LOSER */
598
599 pim_ifchannel_update_assert_tracking_desired(ch);
600 }
601 }
602}
603
604void pim_upstream_update_could_assert(struct pim_upstream *up)
605{
606 struct listnode *ifnode;
607 struct listnode *ifnextnode;
608 struct listnode *chnode;
609 struct listnode *chnextnode;
610 struct interface *ifp;
611 struct pim_interface *pim_ifp;
612 struct pim_ifchannel *ch;
613
614 /* scan all interfaces */
615 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
616 pim_ifp = ifp->info;
617 if (!pim_ifp)
618 continue;
619
620 /* scan per-interface (S,G) state */
621 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
622
623 if (ch->upstream != up)
624 continue;
625
626 pim_ifchannel_update_could_assert(ch);
627
628 } /* scan iface channel list */
629 } /* scan iflist */
630}
631
632void pim_upstream_update_my_assert_metric(struct pim_upstream *up)
633{
634 struct listnode *ifnode;
635 struct listnode *ifnextnode;
636 struct listnode *chnode;
637 struct listnode *chnextnode;
638 struct interface *ifp;
639 struct pim_interface *pim_ifp;
640 struct pim_ifchannel *ch;
641
642 /* scan all interfaces */
643 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
644 pim_ifp = ifp->info;
645 if (!pim_ifp)
646 continue;
647
648 /* scan per-interface (S,G) state */
649 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
650
651 if (ch->upstream != up)
652 continue;
653
654 pim_ifchannel_update_my_assert_metric(ch);
655
656 } /* scan iface channel list */
657 } /* scan iflist */
658}
659
660static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up)
661{
662 struct listnode *ifnode;
663 struct listnode *ifnextnode;
664 struct listnode *chnode;
665 struct listnode *chnextnode;
666 struct interface *ifp;
667 struct pim_interface *pim_ifp;
668 struct pim_ifchannel *ch;
669
670 /* scan all interfaces */
671 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
672 pim_ifp = ifp->info;
673 if (!pim_ifp)
674 continue;
675
676 /* scan per-interface (S,G) state */
677 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
678
679 if (ch->upstream != up)
680 continue;
681
682 pim_ifchannel_update_assert_tracking_desired(ch);
683
684 } /* scan iface channel list */
685 } /* scan iflist */
686}