1 // SPDX-License-Identifier: GPL-2.0-or-later
4 * Copyright (C) 2008 Everton da Silva Marques
14 #include "pim_instance.h"
21 #include "pim_iface.h"
22 #include "pim_hello.h"
23 #include "pim_macro.h"
24 #include "pim_assert.h"
25 #include "pim_zebra.h"
26 #include "pim_ifchannel.h"
28 static int assert_action_a3(struct pim_ifchannel
*ch
);
29 static void assert_action_a2(struct pim_ifchannel
*ch
,
30 struct pim_assert_metric winner_metric
);
31 static void assert_action_a6(struct pim_ifchannel
*ch
,
32 struct pim_assert_metric winner_metric
);
34 void pim_ifassert_winner_set(struct pim_ifchannel
*ch
,
35 enum pim_ifassert_state new_state
, pim_addr winner
,
36 struct pim_assert_metric winner_metric
)
38 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
39 int winner_changed
= !!pim_addr_cmp(ch
->ifassert_winner
, winner
);
40 int metric_changed
= !pim_assert_metric_match(
41 &ch
->ifassert_winner_metric
, &winner_metric
);
42 enum pim_rpf_result rpf_result
;
43 struct pim_rpf old_rpf
;
45 if (PIM_DEBUG_PIM_EVENTS
) {
46 if (ch
->ifassert_state
!= new_state
) {
48 "%s: (S,G)=%s assert state changed from %s to %s on interface %s",
50 pim_ifchannel_ifassert_name(ch
->ifassert_state
),
51 pim_ifchannel_ifassert_name(new_state
),
57 "%s: (S,G)=%s assert winner changed from %pPAs to %pPAs on interface %s",
58 __func__
, ch
->sg_str
, &ch
->ifassert_winner
,
59 &winner
, ch
->interface
->name
);
60 } /* PIM_DEBUG_PIM_EVENTS */
62 ch
->ifassert_state
= new_state
;
63 ch
->ifassert_winner
= winner
;
64 ch
->ifassert_winner_metric
= winner_metric
;
65 ch
->ifassert_creation
= pim_time_monotonic_sec();
67 if (winner_changed
|| metric_changed
) {
69 old_rpf
.source_nexthop
.interface
=
70 ch
->upstream
->rpf
.source_nexthop
.interface
;
71 rpf_result
= pim_rpf_update(pim_ifp
->pim
, ch
->upstream
,
73 if (rpf_result
== PIM_RPF_CHANGED
||
74 (rpf_result
== PIM_RPF_FAILURE
&&
75 old_rpf
.source_nexthop
.interface
))
76 pim_zebra_upstream_rpf_changed(
77 pim_ifp
->pim
, ch
->upstream
, &old_rpf
);
78 /* update kernel multicast forwarding cache (MFC) */
79 if (ch
->upstream
->rpf
.source_nexthop
.interface
&&
80 ch
->upstream
->channel_oil
)
81 pim_upstream_mroute_iif_update(
82 ch
->upstream
->channel_oil
, __func__
);
84 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
85 pim_ifchannel_update_could_assert(ch
);
86 pim_ifchannel_update_assert_tracking_desired(ch
);
90 static void on_trace(const char *label
, struct interface
*ifp
, pim_addr src
)
92 if (PIM_DEBUG_PIM_TRACE
)
93 zlog_debug("%s: from %pPAs on %s", label
, &src
, ifp
->name
);
96 static int preferred_assert(const struct pim_ifchannel
*ch
,
97 const struct pim_assert_metric
*recv_metric
)
99 return pim_assert_metric_better(recv_metric
,
100 &ch
->ifassert_winner_metric
);
103 static int acceptable_assert(const struct pim_assert_metric
*my_metric
,
104 const struct pim_assert_metric
*recv_metric
)
106 return pim_assert_metric_better(recv_metric
, my_metric
);
109 static int inferior_assert(const struct pim_assert_metric
*my_metric
,
110 const struct pim_assert_metric
*recv_metric
)
112 return pim_assert_metric_better(my_metric
, recv_metric
);
115 static int cancel_assert(const struct pim_assert_metric
*recv_metric
)
117 return (recv_metric
->metric_preference
118 == PIM_ASSERT_METRIC_PREFERENCE_MAX
)
119 && (recv_metric
->route_metric
== PIM_ASSERT_ROUTE_METRIC_MAX
);
122 static void if_could_assert_do_a1(const char *caller
, struct pim_ifchannel
*ch
)
124 if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
125 if (assert_action_a1(ch
)) {
127 "%s: %s: (S,G)=%s assert_action_a1 failure on interface %s",
128 __func__
, caller
, ch
->sg_str
,
129 ch
->interface
->name
);
130 /* log warning only */
135 static int dispatch_assert(struct interface
*ifp
, pim_addr source_addr
,
137 struct pim_assert_metric recv_metric
)
139 struct pim_ifchannel
*ch
;
142 memset(&sg
, 0, sizeof(sg
));
143 sg
.src
= source_addr
;
145 ch
= pim_ifchannel_add(ifp
, &sg
, 0, 0);
147 switch (ch
->ifassert_state
) {
148 case PIM_IFASSERT_NOINFO
:
149 if (recv_metric
.rpt_bit_flag
) {
151 if_could_assert_do_a1(__func__
, ch
);
154 if (inferior_assert(&ch
->ifassert_my_metric
,
156 if_could_assert_do_a1(__func__
, ch
);
157 } else if (acceptable_assert(&ch
->ifassert_my_metric
,
159 if (PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(
161 assert_action_a6(ch
, recv_metric
);
166 case PIM_IFASSERT_I_AM_WINNER
:
167 if (preferred_assert(ch
, &recv_metric
)) {
168 assert_action_a2(ch
, recv_metric
);
170 if (inferior_assert(&ch
->ifassert_my_metric
,
172 assert_action_a3(ch
);
176 case PIM_IFASSERT_I_AM_LOSER
:
177 if (!pim_addr_cmp(recv_metric
.ip_address
,
178 ch
->ifassert_winner
)) {
179 /* Assert from current winner */
181 if (cancel_assert(&recv_metric
)) {
182 assert_action_a5(ch
);
184 if (inferior_assert(&ch
->ifassert_my_metric
,
186 assert_action_a5(ch
);
187 } else if (acceptable_assert(
188 &ch
->ifassert_my_metric
,
190 if (!recv_metric
.rpt_bit_flag
) {
196 } else if (preferred_assert(ch
, &recv_metric
)) {
197 assert_action_a2(ch
, recv_metric
);
202 "%s: (S,G)=%s invalid assert state %d on interface %s",
203 __func__
, ch
->sg_str
, ch
->ifassert_state
, ifp
->name
);
211 int pim_assert_recv(struct interface
*ifp
, struct pim_neighbor
*neigh
,
212 pim_addr src_addr
, uint8_t *buf
, int buf_size
)
215 pim_addr msg_source_addr
;
216 bool wrong_af
= false;
217 struct pim_assert_metric msg_metric
;
221 struct pim_interface
*pim_ifp
= NULL
;
223 on_trace(__func__
, ifp
, src_addr
);
226 curr_size
= buf_size
;
229 Parse assert group addr
231 memset(&sg
, 0, sizeof(sg
));
232 offset
= pim_parse_addr_group(&sg
, curr
, curr_size
);
235 "%s: pim_parse_addr_group() failure: from %pPAs on %s",
236 __func__
, &src_addr
, ifp
->name
);
243 Parse assert source addr
245 offset
= pim_parse_addr_ucast(&msg_source_addr
, curr
, curr_size
,
247 if (offset
< 1 || wrong_af
) {
249 "%s: pim_parse_addr_ucast() failure: from %pPAs on %s",
250 __func__
, &src_addr
, ifp
->name
);
258 "%s: preference/metric size is less than 8 bytes: size=%d from %pPAs on interface %s",
259 __func__
, curr_size
, &src_addr
, ifp
->name
);
264 Parse assert metric preference
267 msg_metric
.metric_preference
= pim_read_uint32_host(curr
);
269 msg_metric
.rpt_bit_flag
= msg_metric
.metric_preference
270 & 0x80000000; /* save highest bit */
271 msg_metric
.metric_preference
&= ~0x80000000; /* clear highest bit */
276 Parse assert route metric
279 msg_metric
.route_metric
= pim_read_uint32_host(curr
);
281 if (PIM_DEBUG_PIM_TRACE
)
283 "%s: from %pPAs on %s: (S,G)=(%pPAs,%pPAs) pref=%u metric=%u rpt_bit=%u",
284 __func__
, &src_addr
, ifp
->name
, &msg_source_addr
,
285 &sg
.grp
, msg_metric
.metric_preference
,
286 msg_metric
.route_metric
,
287 PIM_FORCE_BOOLEAN(msg_metric
.rpt_bit_flag
));
289 msg_metric
.ip_address
= src_addr
;
294 if (pim_ifp
->pim_passive_enable
) {
295 if (PIM_DEBUG_PIM_PACKETS
)
297 "skip receiving PIM message on passive interface %s",
302 ++pim_ifp
->pim_ifstat_assert_recv
;
304 return dispatch_assert(ifp
, msg_source_addr
, sg
.grp
, msg_metric
);
308 RFC 4601: 4.6.3. Assert Metrics
310 Assert metrics are defined as:
312 When comparing assert_metrics, the rpt_bit_flag, metric_preference,
313 and route_metric field are compared in order, where the first lower
314 value wins. If all fields are equal, the primary IP address of the
315 router that sourced the Assert message is used as a tie-breaker,
316 with the highest IP address winning.
318 int pim_assert_metric_better(const struct pim_assert_metric
*m1
,
319 const struct pim_assert_metric
*m2
)
321 if (m1
->rpt_bit_flag
< m2
->rpt_bit_flag
)
323 if (m1
->rpt_bit_flag
> m2
->rpt_bit_flag
)
326 if (m1
->metric_preference
< m2
->metric_preference
)
328 if (m1
->metric_preference
> m2
->metric_preference
)
331 if (m1
->route_metric
< m2
->route_metric
)
333 if (m1
->route_metric
> m2
->route_metric
)
336 return pim_addr_cmp(m1
->ip_address
, m2
->ip_address
) > 0;
339 int pim_assert_metric_match(const struct pim_assert_metric
*m1
,
340 const struct pim_assert_metric
*m2
)
342 if (m1
->rpt_bit_flag
!= m2
->rpt_bit_flag
)
344 if (m1
->metric_preference
!= m2
->metric_preference
)
346 if (m1
->route_metric
!= m2
->route_metric
)
349 return !pim_addr_cmp(m1
->ip_address
, m2
->ip_address
);
352 int pim_assert_build_msg(uint8_t *pim_msg
, int buf_size
, struct interface
*ifp
,
353 pim_addr group_addr
, pim_addr source_addr
,
354 uint32_t metric_preference
, uint32_t route_metric
,
355 uint32_t rpt_bit_flag
)
357 struct pim_interface
*pim_ifp
= ifp
->info
;
358 uint8_t *buf_pastend
= pim_msg
+ buf_size
;
359 uint8_t *pim_msg_curr
;
364 pim_msg
+ PIM_MSG_HEADER_LEN
; /* skip room for pim header */
367 remain
= buf_pastend
- pim_msg_curr
;
368 pim_msg_curr
= pim_msg_addr_encode_group(pim_msg_curr
, group_addr
);
371 "%s: failure encoding group address %pPA: space left=%d",
372 __func__
, &group_addr
, remain
);
377 remain
= buf_pastend
- pim_msg_curr
;
378 pim_msg_curr
= pim_msg_addr_encode_ucast(pim_msg_curr
, source_addr
);
381 "%s: failure encoding source address %pPA: space left=%d",
382 __func__
, &source_addr
, remain
);
386 /* Metric preference */
387 pim_write_uint32(pim_msg_curr
,
388 rpt_bit_flag
? metric_preference
| 0x80000000
389 : metric_preference
);
393 pim_write_uint32(pim_msg_curr
, route_metric
);
399 pim_msg_size
= pim_msg_curr
- pim_msg
;
400 pim_msg_build_header(pim_ifp
->primary_address
,
401 qpim_all_pim_routers_addr
, pim_msg
, pim_msg_size
,
402 PIM_MSG_TYPE_ASSERT
, false);
407 static int pim_assert_do(struct pim_ifchannel
*ch
,
408 struct pim_assert_metric metric
)
410 struct interface
*ifp
;
411 struct pim_interface
*pim_ifp
;
412 uint8_t pim_msg
[1000];
417 if (PIM_DEBUG_PIM_TRACE
)
418 zlog_debug("%s: channel%s has no associated interface!",
419 __func__
, ch
->sg_str
);
424 if (PIM_DEBUG_PIM_TRACE
)
426 "%s: channel %s pim not enabled on interface: %s",
427 __func__
, ch
->sg_str
, ifp
->name
);
432 pim_assert_build_msg(pim_msg
, sizeof(pim_msg
), ifp
, ch
->sg
.grp
,
433 ch
->sg
.src
, metric
.metric_preference
,
434 metric
.route_metric
, metric
.rpt_bit_flag
);
435 if (pim_msg_size
< 1) {
437 "%s: failure building PIM assert message: msg_size=%d",
438 __func__
, pim_msg_size
);
443 RFC 4601: 4.3.1. Sending Hello Messages
445 Thus, if a router needs to send a Join/Prune or Assert message on
446 an interface on which it has not yet sent a Hello message with the
447 currently configured IP address, then it MUST immediately send the
448 relevant Hello message without waiting for the Hello Timer to
449 expire, followed by the Join/Prune or Assert message.
451 pim_hello_require(ifp
);
453 if (PIM_DEBUG_PIM_TRACE
) {
454 zlog_debug("%s: to %s: (S,G)=%s pref=%u metric=%u rpt_bit=%u",
455 __func__
, ifp
->name
, ch
->sg_str
,
456 metric
.metric_preference
, metric
.route_metric
,
457 PIM_FORCE_BOOLEAN(metric
.rpt_bit_flag
));
459 if (!pim_ifp
->pim_passive_enable
)
460 ++pim_ifp
->pim_ifstat_assert_send
;
462 if (pim_msg_send(pim_ifp
->pim_sock_fd
, pim_ifp
->primary_address
,
463 qpim_all_pim_routers_addr
, pim_msg
, pim_msg_size
,
465 zlog_warn("%s: could not send PIM message on interface %s",
466 __func__
, ifp
->name
);
473 int pim_assert_send(struct pim_ifchannel
*ch
)
475 return pim_assert_do(ch
, ch
->ifassert_my_metric
);
479 RFC 4601: 4.6.4. AssertCancel Messages
481 An AssertCancel(S,G) is an infinite metric assert with the RPT bit
482 set that names S as the source.
484 static int pim_assert_cancel(struct pim_ifchannel
*ch
)
486 struct pim_assert_metric metric
;
488 metric
.rpt_bit_flag
= 0;
489 metric
.metric_preference
= PIM_ASSERT_METRIC_PREFERENCE_MAX
;
490 metric
.route_metric
= PIM_ASSERT_ROUTE_METRIC_MAX
;
491 metric
.ip_address
= ch
->sg
.src
;
493 return pim_assert_do(ch
, metric
);
496 static void on_assert_timer(struct event
*t
)
498 struct pim_ifchannel
*ch
;
499 struct interface
*ifp
;
505 if (PIM_DEBUG_PIM_TRACE
) {
506 zlog_debug("%s: (S,G)=%s timer expired on interface %s",
507 __func__
, ch
->sg_str
, ifp
->name
);
510 ch
->t_ifassert_timer
= NULL
;
512 switch (ch
->ifassert_state
) {
513 case PIM_IFASSERT_I_AM_WINNER
:
514 assert_action_a3(ch
);
516 case PIM_IFASSERT_I_AM_LOSER
:
517 assert_action_a5(ch
);
519 case PIM_IFASSERT_NOINFO
: {
520 if (PIM_DEBUG_PIM_EVENTS
)
522 "%s: (S,G)=%s invalid assert state %d on interface %s",
523 __func__
, ch
->sg_str
, ch
->ifassert_state
,
529 static void assert_timer_off(struct pim_ifchannel
*ch
)
531 if (PIM_DEBUG_PIM_TRACE
) {
532 if (ch
->t_ifassert_timer
) {
534 "%s: (S,G)=%s cancelling timer on interface %s",
535 __func__
, ch
->sg_str
, ch
->interface
->name
);
538 EVENT_OFF(ch
->t_ifassert_timer
);
541 static void pim_assert_timer_set(struct pim_ifchannel
*ch
, int interval
)
543 assert_timer_off(ch
);
545 if (PIM_DEBUG_PIM_TRACE
) {
546 zlog_debug("%s: (S,G)=%s starting %u sec timer on interface %s",
547 __func__
, ch
->sg_str
, interval
, ch
->interface
->name
);
550 event_add_timer(router
->master
, on_assert_timer
, ch
, interval
,
551 &ch
->t_ifassert_timer
);
554 static void pim_assert_timer_reset(struct pim_ifchannel
*ch
)
556 pim_assert_timer_set(ch
,
557 PIM_ASSERT_TIME
- PIM_ASSERT_OVERRIDE_INTERVAL
);
561 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
563 (S,G) Assert State machine Actions
565 A1: Send Assert(S,G).
566 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
567 Store self as AssertWinner(S,G,I).
568 Store spt_assert_metric(S,I) as AssertWinnerMetric(S,G,I).
570 int assert_action_a1(struct pim_ifchannel
*ch
)
572 struct interface
*ifp
= ch
->interface
;
573 struct pim_interface
*pim_ifp
;
577 zlog_warn("%s: (S,G)=%s multicast not enabled on interface %s",
578 __func__
, ch
->sg_str
, ifp
->name
);
579 return -1; /* must return since pim_ifp is used below */
582 /* Switch to I_AM_WINNER before performing action_a3 below */
583 pim_ifassert_winner_set(
584 ch
, PIM_IFASSERT_I_AM_WINNER
, pim_ifp
->primary_address
,
585 pim_macro_spt_assert_metric(&ch
->upstream
->rpf
,
586 pim_ifp
->primary_address
));
588 if (assert_action_a3(ch
)) {
590 "%s: (S,G)=%s assert_action_a3 failure on interface %s",
591 __func__
, ch
->sg_str
, ifp
->name
);
595 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_WINNER
) {
596 if (PIM_DEBUG_PIM_EVENTS
)
598 "%s: channel%s not in expected PIM_IFASSERT_I_AM_WINNER state",
599 __func__
, ch
->sg_str
);
606 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
608 (S,G) Assert State machine Actions
610 A2: Store new assert winner as AssertWinner(S,G,I) and assert
611 winner metric as AssertWinnerMetric(S,G,I).
612 Set Assert Timer to Assert_Time.
614 static void assert_action_a2(struct pim_ifchannel
*ch
,
615 struct pim_assert_metric winner_metric
)
617 pim_ifassert_winner_set(ch
, PIM_IFASSERT_I_AM_LOSER
,
618 winner_metric
.ip_address
, winner_metric
);
620 pim_assert_timer_set(ch
, PIM_ASSERT_TIME
);
622 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_LOSER
) {
623 if (PIM_DEBUG_PIM_EVENTS
)
625 "%s: channel%s not in expected PIM_IFASSERT_I_AM_LOSER state",
626 __func__
, ch
->sg_str
);
631 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
633 (S,G) Assert State machine Actions
635 A3: Send Assert(S,G).
636 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
638 static int assert_action_a3(struct pim_ifchannel
*ch
)
640 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_WINNER
) {
641 if (PIM_DEBUG_PIM_EVENTS
)
643 "%s: channel%s expected to be in PIM_IFASSERT_I_AM_WINNER state",
644 __func__
, ch
->sg_str
);
648 pim_assert_timer_reset(ch
);
650 if (pim_assert_send(ch
)) {
651 zlog_warn("%s: (S,G)=%s failure sending assert on interface %s",
652 __func__
, ch
->sg_str
, ch
->interface
->name
);
660 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
662 (S,G) Assert State machine Actions
664 A4: Send AssertCancel(S,G).
665 Delete assert info (AssertWinner(S,G,I) and
666 AssertWinnerMetric(S,G,I) will then return their default
669 void assert_action_a4(struct pim_ifchannel
*ch
)
671 if (pim_assert_cancel(ch
)) {
672 zlog_warn("%s: failure sending AssertCancel%s on interface %s",
673 __func__
, ch
->sg_str
, ch
->interface
->name
);
674 /* log warning only */
677 assert_action_a5(ch
);
679 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
680 if (PIM_DEBUG_PIM_EVENTS
)
682 "%s: channel%s not in PIM_IFASSERT_NOINFO state as expected",
683 __func__
, ch
->sg_str
);
688 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
690 (S,G) Assert State machine Actions
692 A5: Delete assert info (AssertWinner(S,G,I) and
693 AssertWinnerMetric(S,G,I) will then return their default values).
695 void assert_action_a5(struct pim_ifchannel
*ch
)
697 reset_ifassert_state(ch
);
698 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
699 if (PIM_DEBUG_PIM_EVENTS
)
701 "%s: channel%s not in PIM_IFSSERT_NOINFO state as expected",
702 __func__
, ch
->sg_str
);
707 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
709 (S,G) Assert State machine Actions
711 A6: Store new assert winner as AssertWinner(S,G,I) and assert
712 winner metric as AssertWinnerMetric(S,G,I).
713 Set Assert Timer to Assert_Time.
714 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true)
715 set SPTbit(S,G) to true.
717 static void assert_action_a6(struct pim_ifchannel
*ch
,
718 struct pim_assert_metric winner_metric
)
720 assert_action_a2(ch
, winner_metric
);
723 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) set
726 if (ch
->upstream
->rpf
.source_nexthop
.interface
== ch
->interface
)
727 if (ch
->upstream
->join_state
== PIM_UPSTREAM_JOINED
)
728 ch
->upstream
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
730 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_LOSER
) {
731 if (PIM_DEBUG_PIM_EVENTS
)
733 "%s: channel%s not in PIM_IFASSERT_I_AM_LOSER state as expected",
734 __func__
, ch
->sg_str
);