3 * Copyright (C) 2008 Everton da Silva Marques
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.
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.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27 #include "pim_instance.h"
34 #include "pim_iface.h"
35 #include "pim_hello.h"
36 #include "pim_macro.h"
37 #include "pim_assert.h"
38 #include "pim_zebra.h"
39 #include "pim_ifchannel.h"
41 static int assert_action_a3(struct pim_ifchannel
*ch
);
42 static void assert_action_a2(struct pim_ifchannel
*ch
,
43 struct pim_assert_metric winner_metric
);
44 static void assert_action_a6(struct pim_ifchannel
*ch
,
45 struct pim_assert_metric winner_metric
);
47 void pim_ifassert_winner_set(struct pim_ifchannel
*ch
,
48 enum pim_ifassert_state new_state
, pim_addr winner
,
49 struct pim_assert_metric winner_metric
)
51 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
52 int winner_changed
= !!pim_addr_cmp(ch
->ifassert_winner
, winner
);
53 int metric_changed
= !pim_assert_metric_match(
54 &ch
->ifassert_winner_metric
, &winner_metric
);
55 enum pim_rpf_result rpf_result
;
56 struct pim_rpf old_rpf
;
58 if (PIM_DEBUG_PIM_EVENTS
) {
59 if (ch
->ifassert_state
!= new_state
) {
61 "%s: (S,G)=%s assert state changed from %s to %s on interface %s",
63 pim_ifchannel_ifassert_name(ch
->ifassert_state
),
64 pim_ifchannel_ifassert_name(new_state
),
70 "%s: (S,G)=%s assert winner changed from %pPAs to %pPAs on interface %s",
71 __func__
, ch
->sg_str
, &ch
->ifassert_winner
,
72 &winner
, ch
->interface
->name
);
73 } /* PIM_DEBUG_PIM_EVENTS */
75 ch
->ifassert_state
= new_state
;
76 ch
->ifassert_winner
= winner
;
77 ch
->ifassert_winner_metric
= winner_metric
;
78 ch
->ifassert_creation
= pim_time_monotonic_sec();
80 if (winner_changed
|| metric_changed
) {
82 old_rpf
.source_nexthop
.interface
=
83 ch
->upstream
->rpf
.source_nexthop
.interface
;
84 rpf_result
= pim_rpf_update(pim_ifp
->pim
, ch
->upstream
,
86 if (rpf_result
== PIM_RPF_CHANGED
||
87 (rpf_result
== PIM_RPF_FAILURE
&&
88 old_rpf
.source_nexthop
.interface
))
89 pim_zebra_upstream_rpf_changed(
90 pim_ifp
->pim
, ch
->upstream
, &old_rpf
);
91 /* update kernel multicast forwarding cache (MFC) */
92 if (ch
->upstream
->rpf
.source_nexthop
.interface
&&
93 ch
->upstream
->channel_oil
)
94 pim_upstream_mroute_iif_update(
95 ch
->upstream
->channel_oil
, __func__
);
97 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
98 pim_ifchannel_update_could_assert(ch
);
99 pim_ifchannel_update_assert_tracking_desired(ch
);
103 static void on_trace(const char *label
, struct interface
*ifp
, pim_addr src
)
105 if (PIM_DEBUG_PIM_TRACE
)
106 zlog_debug("%s: from %pPAs on %s", label
, &src
, ifp
->name
);
109 static int preferred_assert(const struct pim_ifchannel
*ch
,
110 const struct pim_assert_metric
*recv_metric
)
112 return pim_assert_metric_better(recv_metric
,
113 &ch
->ifassert_winner_metric
);
116 static int acceptable_assert(const struct pim_assert_metric
*my_metric
,
117 const struct pim_assert_metric
*recv_metric
)
119 return pim_assert_metric_better(recv_metric
, my_metric
);
122 static int inferior_assert(const struct pim_assert_metric
*my_metric
,
123 const struct pim_assert_metric
*recv_metric
)
125 return pim_assert_metric_better(my_metric
, recv_metric
);
128 static int cancel_assert(const struct pim_assert_metric
*recv_metric
)
130 return (recv_metric
->metric_preference
131 == PIM_ASSERT_METRIC_PREFERENCE_MAX
)
132 && (recv_metric
->route_metric
== PIM_ASSERT_ROUTE_METRIC_MAX
);
135 static void if_could_assert_do_a1(const char *caller
, struct pim_ifchannel
*ch
)
137 if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
138 if (assert_action_a1(ch
)) {
140 "%s: %s: (S,G)=%s assert_action_a1 failure on interface %s",
141 __func__
, caller
, ch
->sg_str
,
142 ch
->interface
->name
);
143 /* log warning only */
148 static int dispatch_assert(struct interface
*ifp
, pim_addr source_addr
,
150 struct pim_assert_metric recv_metric
)
152 struct pim_ifchannel
*ch
;
155 memset(&sg
, 0, sizeof(sg
));
156 sg
.src
= source_addr
;
158 ch
= pim_ifchannel_add(ifp
, &sg
, 0, 0);
160 switch (ch
->ifassert_state
) {
161 case PIM_IFASSERT_NOINFO
:
162 if (recv_metric
.rpt_bit_flag
) {
164 if_could_assert_do_a1(__func__
, ch
);
167 if (inferior_assert(&ch
->ifassert_my_metric
,
169 if_could_assert_do_a1(__func__
, ch
);
170 } else if (acceptable_assert(&ch
->ifassert_my_metric
,
172 if (PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(
174 assert_action_a6(ch
, recv_metric
);
179 case PIM_IFASSERT_I_AM_WINNER
:
180 if (preferred_assert(ch
, &recv_metric
)) {
181 assert_action_a2(ch
, recv_metric
);
183 if (inferior_assert(&ch
->ifassert_my_metric
,
185 assert_action_a3(ch
);
189 case PIM_IFASSERT_I_AM_LOSER
:
190 if (!pim_addr_cmp(recv_metric
.ip_address
,
191 ch
->ifassert_winner
)) {
192 /* Assert from current winner */
194 if (cancel_assert(&recv_metric
)) {
195 assert_action_a5(ch
);
197 if (inferior_assert(&ch
->ifassert_my_metric
,
199 assert_action_a5(ch
);
200 } else if (acceptable_assert(
201 &ch
->ifassert_my_metric
,
203 if (!recv_metric
.rpt_bit_flag
) {
209 } else if (preferred_assert(ch
, &recv_metric
)) {
210 assert_action_a2(ch
, recv_metric
);
215 "%s: (S,G)=%s invalid assert state %d on interface %s",
216 __func__
, ch
->sg_str
, ch
->ifassert_state
, ifp
->name
);
224 int pim_assert_recv(struct interface
*ifp
, struct pim_neighbor
*neigh
,
225 pim_addr src_addr
, uint8_t *buf
, int buf_size
)
228 pim_addr msg_source_addr
;
229 bool wrong_af
= false;
230 struct pim_assert_metric msg_metric
;
234 struct pim_interface
*pim_ifp
= NULL
;
236 on_trace(__func__
, ifp
, src_addr
);
239 curr_size
= buf_size
;
242 Parse assert group addr
244 memset(&sg
, 0, sizeof(sg
));
245 offset
= pim_parse_addr_group(&sg
, curr
, curr_size
);
248 "%s: pim_parse_addr_group() failure: from %pPAs on %s",
249 __func__
, &src_addr
, ifp
->name
);
256 Parse assert source addr
258 offset
= pim_parse_addr_ucast(&msg_source_addr
, curr
, curr_size
,
260 if (offset
< 1 || wrong_af
) {
262 "%s: pim_parse_addr_ucast() failure: from %pPAs on %s",
263 __func__
, &src_addr
, ifp
->name
);
271 "%s: preference/metric size is less than 8 bytes: size=%d from %pPAs on interface %s",
272 __func__
, curr_size
, &src_addr
, ifp
->name
);
277 Parse assert metric preference
280 msg_metric
.metric_preference
= pim_read_uint32_host(curr
);
282 msg_metric
.rpt_bit_flag
= msg_metric
.metric_preference
283 & 0x80000000; /* save highest bit */
284 msg_metric
.metric_preference
&= ~0x80000000; /* clear highest bit */
289 Parse assert route metric
292 msg_metric
.route_metric
= pim_read_uint32_host(curr
);
294 if (PIM_DEBUG_PIM_TRACE
)
296 "%s: from %pPAs on %s: (S,G)=(%pPAs,%pPAs) pref=%u metric=%u rpt_bit=%u",
297 __func__
, &src_addr
, ifp
->name
, &msg_source_addr
,
298 &sg
.grp
, msg_metric
.metric_preference
,
299 msg_metric
.route_metric
,
300 PIM_FORCE_BOOLEAN(msg_metric
.rpt_bit_flag
));
302 msg_metric
.ip_address
= src_addr
;
307 if (pim_ifp
->pim_passive_enable
) {
308 if (PIM_DEBUG_PIM_PACKETS
)
310 "skip receiving PIM message on passive interface %s",
315 ++pim_ifp
->pim_ifstat_assert_recv
;
317 return dispatch_assert(ifp
, msg_source_addr
, sg
.grp
, msg_metric
);
321 RFC 4601: 4.6.3. Assert Metrics
323 Assert metrics are defined as:
325 When comparing assert_metrics, the rpt_bit_flag, metric_preference,
326 and route_metric field are compared in order, where the first lower
327 value wins. If all fields are equal, the primary IP address of the
328 router that sourced the Assert message is used as a tie-breaker,
329 with the highest IP address winning.
331 int pim_assert_metric_better(const struct pim_assert_metric
*m1
,
332 const struct pim_assert_metric
*m2
)
334 if (m1
->rpt_bit_flag
< m2
->rpt_bit_flag
)
336 if (m1
->rpt_bit_flag
> m2
->rpt_bit_flag
)
339 if (m1
->metric_preference
< m2
->metric_preference
)
341 if (m1
->metric_preference
> m2
->metric_preference
)
344 if (m1
->route_metric
< m2
->route_metric
)
346 if (m1
->route_metric
> m2
->route_metric
)
349 return pim_addr_cmp(m1
->ip_address
, m2
->ip_address
) > 0;
352 int pim_assert_metric_match(const struct pim_assert_metric
*m1
,
353 const struct pim_assert_metric
*m2
)
355 if (m1
->rpt_bit_flag
!= m2
->rpt_bit_flag
)
357 if (m1
->metric_preference
!= m2
->metric_preference
)
359 if (m1
->route_metric
!= m2
->route_metric
)
362 return !pim_addr_cmp(m1
->ip_address
, m2
->ip_address
);
365 int pim_assert_build_msg(uint8_t *pim_msg
, int buf_size
, struct interface
*ifp
,
366 pim_addr group_addr
, pim_addr source_addr
,
367 uint32_t metric_preference
, uint32_t route_metric
,
368 uint32_t rpt_bit_flag
)
370 struct pim_interface
*pim_ifp
= ifp
->info
;
371 uint8_t *buf_pastend
= pim_msg
+ buf_size
;
372 uint8_t *pim_msg_curr
;
377 pim_msg
+ PIM_MSG_HEADER_LEN
; /* skip room for pim header */
380 remain
= buf_pastend
- pim_msg_curr
;
381 pim_msg_curr
= pim_msg_addr_encode_group(pim_msg_curr
, group_addr
);
384 "%s: failure encoding group address %pPA: space left=%d",
385 __func__
, &group_addr
, remain
);
390 remain
= buf_pastend
- pim_msg_curr
;
391 pim_msg_curr
= pim_msg_addr_encode_ucast(pim_msg_curr
, source_addr
);
394 "%s: failure encoding source address %pPA: space left=%d",
395 __func__
, &source_addr
, remain
);
399 /* Metric preference */
400 pim_write_uint32(pim_msg_curr
,
401 rpt_bit_flag
? metric_preference
| 0x80000000
402 : metric_preference
);
406 pim_write_uint32(pim_msg_curr
, route_metric
);
412 pim_msg_size
= pim_msg_curr
- pim_msg
;
413 pim_msg_build_header(pim_ifp
->primary_address
,
414 qpim_all_pim_routers_addr
, pim_msg
, pim_msg_size
,
415 PIM_MSG_TYPE_ASSERT
, false);
420 static int pim_assert_do(struct pim_ifchannel
*ch
,
421 struct pim_assert_metric metric
)
423 struct interface
*ifp
;
424 struct pim_interface
*pim_ifp
;
425 uint8_t pim_msg
[1000];
430 if (PIM_DEBUG_PIM_TRACE
)
431 zlog_debug("%s: channel%s has no associated interface!",
432 __func__
, ch
->sg_str
);
437 if (PIM_DEBUG_PIM_TRACE
)
439 "%s: channel %s pim not enabled on interface: %s",
440 __func__
, ch
->sg_str
, ifp
->name
);
445 pim_assert_build_msg(pim_msg
, sizeof(pim_msg
), ifp
, ch
->sg
.grp
,
446 ch
->sg
.src
, metric
.metric_preference
,
447 metric
.route_metric
, metric
.rpt_bit_flag
);
448 if (pim_msg_size
< 1) {
450 "%s: failure building PIM assert message: msg_size=%d",
451 __func__
, pim_msg_size
);
456 RFC 4601: 4.3.1. Sending Hello Messages
458 Thus, if a router needs to send a Join/Prune or Assert message on
459 an interface on which it has not yet sent a Hello message with the
460 currently configured IP address, then it MUST immediately send the
461 relevant Hello message without waiting for the Hello Timer to
462 expire, followed by the Join/Prune or Assert message.
464 pim_hello_require(ifp
);
466 if (PIM_DEBUG_PIM_TRACE
) {
467 zlog_debug("%s: to %s: (S,G)=%s pref=%u metric=%u rpt_bit=%u",
468 __func__
, ifp
->name
, ch
->sg_str
,
469 metric
.metric_preference
, metric
.route_metric
,
470 PIM_FORCE_BOOLEAN(metric
.rpt_bit_flag
));
472 if (!pim_ifp
->pim_passive_enable
)
473 ++pim_ifp
->pim_ifstat_assert_send
;
475 if (pim_msg_send(pim_ifp
->pim_sock_fd
, pim_ifp
->primary_address
,
476 qpim_all_pim_routers_addr
, pim_msg
, pim_msg_size
,
478 zlog_warn("%s: could not send PIM message on interface %s",
479 __func__
, ifp
->name
);
486 int pim_assert_send(struct pim_ifchannel
*ch
)
488 return pim_assert_do(ch
, ch
->ifassert_my_metric
);
492 RFC 4601: 4.6.4. AssertCancel Messages
494 An AssertCancel(S,G) is an infinite metric assert with the RPT bit
495 set that names S as the source.
497 static int pim_assert_cancel(struct pim_ifchannel
*ch
)
499 struct pim_assert_metric metric
;
501 metric
.rpt_bit_flag
= 0;
502 metric
.metric_preference
= PIM_ASSERT_METRIC_PREFERENCE_MAX
;
503 metric
.route_metric
= PIM_ASSERT_ROUTE_METRIC_MAX
;
504 metric
.ip_address
= ch
->sg
.src
;
506 return pim_assert_do(ch
, metric
);
509 static void on_assert_timer(struct thread
*t
)
511 struct pim_ifchannel
*ch
;
512 struct interface
*ifp
;
518 if (PIM_DEBUG_PIM_TRACE
) {
519 zlog_debug("%s: (S,G)=%s timer expired on interface %s",
520 __func__
, ch
->sg_str
, ifp
->name
);
523 ch
->t_ifassert_timer
= NULL
;
525 switch (ch
->ifassert_state
) {
526 case PIM_IFASSERT_I_AM_WINNER
:
527 assert_action_a3(ch
);
529 case PIM_IFASSERT_I_AM_LOSER
:
530 assert_action_a5(ch
);
532 case PIM_IFASSERT_NOINFO
: {
533 if (PIM_DEBUG_PIM_EVENTS
)
535 "%s: (S,G)=%s invalid assert state %d on interface %s",
536 __func__
, ch
->sg_str
, ch
->ifassert_state
,
542 static void assert_timer_off(struct pim_ifchannel
*ch
)
544 if (PIM_DEBUG_PIM_TRACE
) {
545 if (ch
->t_ifassert_timer
) {
547 "%s: (S,G)=%s cancelling timer on interface %s",
548 __func__
, ch
->sg_str
, ch
->interface
->name
);
551 THREAD_OFF(ch
->t_ifassert_timer
);
554 static void pim_assert_timer_set(struct pim_ifchannel
*ch
, int interval
)
556 assert_timer_off(ch
);
558 if (PIM_DEBUG_PIM_TRACE
) {
559 zlog_debug("%s: (S,G)=%s starting %u sec timer on interface %s",
560 __func__
, ch
->sg_str
, interval
, ch
->interface
->name
);
563 thread_add_timer(router
->master
, on_assert_timer
, ch
, interval
,
564 &ch
->t_ifassert_timer
);
567 static void pim_assert_timer_reset(struct pim_ifchannel
*ch
)
569 pim_assert_timer_set(ch
,
570 PIM_ASSERT_TIME
- PIM_ASSERT_OVERRIDE_INTERVAL
);
574 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
576 (S,G) Assert State machine Actions
578 A1: Send Assert(S,G).
579 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
580 Store self as AssertWinner(S,G,I).
581 Store spt_assert_metric(S,I) as AssertWinnerMetric(S,G,I).
583 int assert_action_a1(struct pim_ifchannel
*ch
)
585 struct interface
*ifp
= ch
->interface
;
586 struct pim_interface
*pim_ifp
;
590 zlog_warn("%s: (S,G)=%s multicast not enabled on interface %s",
591 __func__
, ch
->sg_str
, ifp
->name
);
592 return -1; /* must return since pim_ifp is used below */
595 /* Switch to I_AM_WINNER before performing action_a3 below */
596 pim_ifassert_winner_set(
597 ch
, PIM_IFASSERT_I_AM_WINNER
, pim_ifp
->primary_address
,
598 pim_macro_spt_assert_metric(&ch
->upstream
->rpf
,
599 pim_ifp
->primary_address
));
601 if (assert_action_a3(ch
)) {
603 "%s: (S,G)=%s assert_action_a3 failure on interface %s",
604 __func__
, ch
->sg_str
, ifp
->name
);
608 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_WINNER
) {
609 if (PIM_DEBUG_PIM_EVENTS
)
611 "%s: channel%s not in expected PIM_IFASSERT_I_AM_WINNER state",
612 __func__
, ch
->sg_str
);
619 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
621 (S,G) Assert State machine Actions
623 A2: Store new assert winner as AssertWinner(S,G,I) and assert
624 winner metric as AssertWinnerMetric(S,G,I).
625 Set Assert Timer to Assert_Time.
627 static void assert_action_a2(struct pim_ifchannel
*ch
,
628 struct pim_assert_metric winner_metric
)
630 pim_ifassert_winner_set(ch
, PIM_IFASSERT_I_AM_LOSER
,
631 winner_metric
.ip_address
, winner_metric
);
633 pim_assert_timer_set(ch
, PIM_ASSERT_TIME
);
635 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_LOSER
) {
636 if (PIM_DEBUG_PIM_EVENTS
)
638 "%s: channel%s not in expected PIM_IFASSERT_I_AM_LOSER state",
639 __func__
, ch
->sg_str
);
644 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
646 (S,G) Assert State machine Actions
648 A3: Send Assert(S,G).
649 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
651 static int assert_action_a3(struct pim_ifchannel
*ch
)
653 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_WINNER
) {
654 if (PIM_DEBUG_PIM_EVENTS
)
656 "%s: channel%s expected to be in PIM_IFASSERT_I_AM_WINNER state",
657 __func__
, ch
->sg_str
);
661 pim_assert_timer_reset(ch
);
663 if (pim_assert_send(ch
)) {
664 zlog_warn("%s: (S,G)=%s failure sending assert on interface %s",
665 __func__
, ch
->sg_str
, ch
->interface
->name
);
673 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
675 (S,G) Assert State machine Actions
677 A4: Send AssertCancel(S,G).
678 Delete assert info (AssertWinner(S,G,I) and
679 AssertWinnerMetric(S,G,I) will then return their default
682 void assert_action_a4(struct pim_ifchannel
*ch
)
684 if (pim_assert_cancel(ch
)) {
685 zlog_warn("%s: failure sending AssertCancel%s on interface %s",
686 __func__
, ch
->sg_str
, ch
->interface
->name
);
687 /* log warning only */
690 assert_action_a5(ch
);
692 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
693 if (PIM_DEBUG_PIM_EVENTS
)
695 "%s: channel%s not in PIM_IFASSERT_NOINFO state as expected",
696 __func__
, ch
->sg_str
);
701 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
703 (S,G) Assert State machine Actions
705 A5: Delete assert info (AssertWinner(S,G,I) and
706 AssertWinnerMetric(S,G,I) will then return their default values).
708 void assert_action_a5(struct pim_ifchannel
*ch
)
710 reset_ifassert_state(ch
);
711 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
712 if (PIM_DEBUG_PIM_EVENTS
)
714 "%s: channel%s not in PIM_IFSSERT_NOINFO state as expected",
715 __func__
, ch
->sg_str
);
720 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
722 (S,G) Assert State machine Actions
724 A6: Store new assert winner as AssertWinner(S,G,I) and assert
725 winner metric as AssertWinnerMetric(S,G,I).
726 Set Assert Timer to Assert_Time.
727 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true)
728 set SPTbit(S,G) to true.
730 static void assert_action_a6(struct pim_ifchannel
*ch
,
731 struct pim_assert_metric winner_metric
)
733 assert_action_a2(ch
, winner_metric
);
736 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) set
739 if (ch
->upstream
->rpf
.source_nexthop
.interface
== ch
->interface
)
740 if (ch
->upstream
->join_state
== PIM_UPSTREAM_JOINED
)
741 ch
->upstream
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
743 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_LOSER
) {
744 if (PIM_DEBUG_PIM_EVENTS
)
746 "%s: channel%s not in PIM_IFASSERT_I_AM_LOSER state as expected",
747 __func__
, ch
->sg_str
);