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
33 #include "pim_iface.h"
34 #include "pim_hello.h"
35 #include "pim_macro.h"
36 #include "pim_assert.h"
37 #include "pim_ifchannel.h"
39 static int assert_action_a3(struct pim_ifchannel
*ch
);
40 static void assert_action_a2(struct pim_ifchannel
*ch
,
41 struct pim_assert_metric winner_metric
);
42 static void assert_action_a6(struct pim_ifchannel
*ch
,
43 struct pim_assert_metric winner_metric
);
45 void pim_ifassert_winner_set(struct pim_ifchannel
*ch
,
46 enum pim_ifassert_state new_state
, pim_addr winner
,
47 struct pim_assert_metric winner_metric
)
49 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
50 int winner_changed
= !!pim_addr_cmp(ch
->ifassert_winner
, winner
);
51 int metric_changed
= !pim_assert_metric_match(
52 &ch
->ifassert_winner_metric
, &winner_metric
);
54 if (PIM_DEBUG_PIM_EVENTS
) {
55 if (ch
->ifassert_state
!= new_state
) {
57 "%s: (S,G)=%s assert state changed from %s to %s on interface %s",
59 pim_ifchannel_ifassert_name(ch
->ifassert_state
),
60 pim_ifchannel_ifassert_name(new_state
),
66 "%s: (S,G)=%s assert winner changed from %pPAs to %pPAs on interface %s",
67 __func__
, ch
->sg_str
, &ch
->ifassert_winner
,
68 &winner
, ch
->interface
->name
);
69 } /* PIM_DEBUG_PIM_EVENTS */
71 ch
->ifassert_state
= new_state
;
72 ch
->ifassert_winner
= winner
;
73 ch
->ifassert_winner_metric
= winner_metric
;
74 ch
->ifassert_creation
= pim_time_monotonic_sec();
76 if (winner_changed
|| metric_changed
) {
77 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
78 pim_ifchannel_update_could_assert(ch
);
79 pim_ifchannel_update_assert_tracking_desired(ch
);
83 static void on_trace(const char *label
, struct interface
*ifp
, pim_addr src
)
85 if (PIM_DEBUG_PIM_TRACE
)
86 zlog_debug("%s: from %pPAs on %s", label
, &src
, ifp
->name
);
89 static int preferred_assert(const struct pim_ifchannel
*ch
,
90 const struct pim_assert_metric
*recv_metric
)
92 return pim_assert_metric_better(recv_metric
,
93 &ch
->ifassert_winner_metric
);
96 static int acceptable_assert(const struct pim_assert_metric
*my_metric
,
97 const struct pim_assert_metric
*recv_metric
)
99 return pim_assert_metric_better(recv_metric
, my_metric
);
102 static int inferior_assert(const struct pim_assert_metric
*my_metric
,
103 const struct pim_assert_metric
*recv_metric
)
105 return pim_assert_metric_better(my_metric
, recv_metric
);
108 static int cancel_assert(const struct pim_assert_metric
*recv_metric
)
110 return (recv_metric
->metric_preference
111 == PIM_ASSERT_METRIC_PREFERENCE_MAX
)
112 && (recv_metric
->route_metric
== PIM_ASSERT_ROUTE_METRIC_MAX
);
115 static void if_could_assert_do_a1(const char *caller
, struct pim_ifchannel
*ch
)
117 if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
118 if (assert_action_a1(ch
)) {
120 "%s: %s: (S,G)=%s assert_action_a1 failure on interface %s",
121 __func__
, caller
, ch
->sg_str
,
122 ch
->interface
->name
);
123 /* log warning only */
128 static int dispatch_assert(struct interface
*ifp
, pim_addr source_addr
,
130 struct pim_assert_metric recv_metric
)
132 struct pim_ifchannel
*ch
;
135 memset(&sg
, 0, sizeof(sg
));
136 sg
.src
= source_addr
;
138 ch
= pim_ifchannel_add(ifp
, &sg
, 0, 0);
140 switch (ch
->ifassert_state
) {
141 case PIM_IFASSERT_NOINFO
:
142 if (recv_metric
.rpt_bit_flag
) {
144 if_could_assert_do_a1(__func__
, ch
);
147 if (inferior_assert(&ch
->ifassert_my_metric
,
149 if_could_assert_do_a1(__func__
, ch
);
150 } else if (acceptable_assert(&ch
->ifassert_my_metric
,
152 if (PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(
154 assert_action_a6(ch
, recv_metric
);
159 case PIM_IFASSERT_I_AM_WINNER
:
160 if (preferred_assert(ch
, &recv_metric
)) {
161 assert_action_a2(ch
, recv_metric
);
163 if (inferior_assert(&ch
->ifassert_my_metric
,
165 assert_action_a3(ch
);
169 case PIM_IFASSERT_I_AM_LOSER
:
170 if (!pim_addr_cmp(recv_metric
.ip_address
,
171 ch
->ifassert_winner
)) {
172 /* Assert from current winner */
174 if (cancel_assert(&recv_metric
)) {
175 assert_action_a5(ch
);
177 if (inferior_assert(&ch
->ifassert_my_metric
,
179 assert_action_a5(ch
);
180 } else if (acceptable_assert(
181 &ch
->ifassert_my_metric
,
183 if (!recv_metric
.rpt_bit_flag
) {
189 } else if (preferred_assert(ch
, &recv_metric
)) {
190 assert_action_a2(ch
, recv_metric
);
195 "%s: (S,G)=%s invalid assert state %d on interface %s",
196 __func__
, ch
->sg_str
, ch
->ifassert_state
, ifp
->name
);
204 int pim_assert_recv(struct interface
*ifp
, struct pim_neighbor
*neigh
,
205 pim_addr src_addr
, uint8_t *buf
, int buf_size
)
208 pim_addr msg_source_addr
;
209 bool wrong_af
= false;
210 struct pim_assert_metric msg_metric
;
214 struct pim_interface
*pim_ifp
= NULL
;
216 on_trace(__func__
, ifp
, src_addr
);
219 curr_size
= buf_size
;
222 Parse assert group addr
224 memset(&sg
, 0, sizeof(sg
));
225 offset
= pim_parse_addr_group(&sg
, curr
, curr_size
);
228 "%s: pim_parse_addr_group() failure: from %pPAs on %s",
229 __func__
, &src_addr
, ifp
->name
);
236 Parse assert source addr
238 offset
= pim_parse_addr_ucast(&msg_source_addr
, curr
, curr_size
,
240 if (offset
< 1 || wrong_af
) {
242 "%s: pim_parse_addr_ucast() failure: from %pPAs on %s",
243 __func__
, &src_addr
, ifp
->name
);
251 "%s: preference/metric size is less than 8 bytes: size=%d from %pPAs on interface %s",
252 __func__
, curr_size
, &src_addr
, ifp
->name
);
257 Parse assert metric preference
260 msg_metric
.metric_preference
= pim_read_uint32_host(curr
);
262 msg_metric
.rpt_bit_flag
= msg_metric
.metric_preference
263 & 0x80000000; /* save highest bit */
264 msg_metric
.metric_preference
&= ~0x80000000; /* clear highest bit */
269 Parse assert route metric
272 msg_metric
.route_metric
= pim_read_uint32_host(curr
);
274 if (PIM_DEBUG_PIM_TRACE
)
276 "%s: from %pPAs on %s: (S,G)=(%pPAs,%pPAs) pref=%u metric=%u rpt_bit=%u",
277 __func__
, &src_addr
, ifp
->name
, &msg_source_addr
,
278 &sg
.grp
, msg_metric
.metric_preference
,
279 msg_metric
.route_metric
,
280 PIM_FORCE_BOOLEAN(msg_metric
.rpt_bit_flag
));
282 msg_metric
.ip_address
= src_addr
;
286 ++pim_ifp
->pim_ifstat_assert_recv
;
288 return dispatch_assert(ifp
, msg_source_addr
, sg
.grp
, msg_metric
);
292 RFC 4601: 4.6.3. Assert Metrics
294 Assert metrics are defined as:
296 When comparing assert_metrics, the rpt_bit_flag, metric_preference,
297 and route_metric field are compared in order, where the first lower
298 value wins. If all fields are equal, the primary IP address of the
299 router that sourced the Assert message is used as a tie-breaker,
300 with the highest IP address winning.
302 int pim_assert_metric_better(const struct pim_assert_metric
*m1
,
303 const struct pim_assert_metric
*m2
)
305 if (m1
->rpt_bit_flag
< m2
->rpt_bit_flag
)
307 if (m1
->rpt_bit_flag
> m2
->rpt_bit_flag
)
310 if (m1
->metric_preference
< m2
->metric_preference
)
312 if (m1
->metric_preference
> m2
->metric_preference
)
315 if (m1
->route_metric
< m2
->route_metric
)
317 if (m1
->route_metric
> m2
->route_metric
)
320 return pim_addr_cmp(m1
->ip_address
, m2
->ip_address
) > 0;
323 int pim_assert_metric_match(const struct pim_assert_metric
*m1
,
324 const struct pim_assert_metric
*m2
)
326 if (m1
->rpt_bit_flag
!= m2
->rpt_bit_flag
)
328 if (m1
->metric_preference
!= m2
->metric_preference
)
330 if (m1
->route_metric
!= m2
->route_metric
)
333 return !pim_addr_cmp(m1
->ip_address
, m2
->ip_address
);
336 int pim_assert_build_msg(uint8_t *pim_msg
, int buf_size
, struct interface
*ifp
,
337 pim_addr group_addr
, pim_addr source_addr
,
338 uint32_t metric_preference
, uint32_t route_metric
,
339 uint32_t rpt_bit_flag
)
341 struct pim_interface
*pim_ifp
= ifp
->info
;
342 uint8_t *buf_pastend
= pim_msg
+ buf_size
;
343 uint8_t *pim_msg_curr
;
348 pim_msg
+ PIM_MSG_HEADER_LEN
; /* skip room for pim header */
351 remain
= buf_pastend
- pim_msg_curr
;
352 pim_msg_curr
= pim_msg_addr_encode_group(pim_msg_curr
, group_addr
);
355 "%s: failure encoding group address %pPA: space left=%d",
356 __func__
, &group_addr
, remain
);
361 remain
= buf_pastend
- pim_msg_curr
;
362 pim_msg_curr
= pim_msg_addr_encode_ucast(pim_msg_curr
, source_addr
);
365 "%s: failure encoding source address %pPA: space left=%d",
366 __func__
, &source_addr
, remain
);
370 /* Metric preference */
371 pim_write_uint32(pim_msg_curr
,
372 rpt_bit_flag
? metric_preference
| 0x80000000
373 : metric_preference
);
377 pim_write_uint32(pim_msg_curr
, route_metric
);
383 pim_msg_size
= pim_msg_curr
- pim_msg
;
384 pim_msg_build_header(pim_ifp
->primary_address
,
385 qpim_all_pim_routers_addr
, pim_msg
, pim_msg_size
,
386 PIM_MSG_TYPE_ASSERT
, false);
391 static int pim_assert_do(struct pim_ifchannel
*ch
,
392 struct pim_assert_metric metric
)
394 struct interface
*ifp
;
395 struct pim_interface
*pim_ifp
;
396 uint8_t pim_msg
[1000];
401 if (PIM_DEBUG_PIM_TRACE
)
402 zlog_debug("%s: channel%s has no associated interface!",
403 __func__
, ch
->sg_str
);
408 if (PIM_DEBUG_PIM_TRACE
)
410 "%s: channel %s pim not enabled on interface: %s",
411 __func__
, ch
->sg_str
, ifp
->name
);
416 pim_assert_build_msg(pim_msg
, sizeof(pim_msg
), ifp
, ch
->sg
.grp
,
417 ch
->sg
.src
, metric
.metric_preference
,
418 metric
.route_metric
, metric
.rpt_bit_flag
);
419 if (pim_msg_size
< 1) {
421 "%s: failure building PIM assert message: msg_size=%d",
422 __func__
, pim_msg_size
);
427 RFC 4601: 4.3.1. Sending Hello Messages
429 Thus, if a router needs to send a Join/Prune or Assert message on
430 an interface on which it has not yet sent a Hello message with the
431 currently configured IP address, then it MUST immediately send the
432 relevant Hello message without waiting for the Hello Timer to
433 expire, followed by the Join/Prune or Assert message.
435 pim_hello_require(ifp
);
437 if (PIM_DEBUG_PIM_TRACE
) {
438 zlog_debug("%s: to %s: (S,G)=%s pref=%u metric=%u rpt_bit=%u",
439 __func__
, ifp
->name
, ch
->sg_str
,
440 metric
.metric_preference
, metric
.route_metric
,
441 PIM_FORCE_BOOLEAN(metric
.rpt_bit_flag
));
443 ++pim_ifp
->pim_ifstat_assert_send
;
445 if (pim_msg_send(pim_ifp
->pim_sock_fd
, pim_ifp
->primary_address
,
446 qpim_all_pim_routers_addr
, pim_msg
, pim_msg_size
,
448 zlog_warn("%s: could not send PIM message on interface %s",
449 __func__
, ifp
->name
);
456 int pim_assert_send(struct pim_ifchannel
*ch
)
458 return pim_assert_do(ch
, ch
->ifassert_my_metric
);
462 RFC 4601: 4.6.4. AssertCancel Messages
464 An AssertCancel(S,G) is an infinite metric assert with the RPT bit
465 set that names S as the source.
467 static int pim_assert_cancel(struct pim_ifchannel
*ch
)
469 struct pim_assert_metric metric
;
471 metric
.rpt_bit_flag
= 0;
472 metric
.metric_preference
= PIM_ASSERT_METRIC_PREFERENCE_MAX
;
473 metric
.route_metric
= PIM_ASSERT_ROUTE_METRIC_MAX
;
474 metric
.ip_address
= ch
->sg
.src
;
476 return pim_assert_do(ch
, metric
);
479 static void on_assert_timer(struct thread
*t
)
481 struct pim_ifchannel
*ch
;
482 struct interface
*ifp
;
488 if (PIM_DEBUG_PIM_TRACE
) {
489 zlog_debug("%s: (S,G)=%s timer expired on interface %s",
490 __func__
, ch
->sg_str
, ifp
->name
);
493 ch
->t_ifassert_timer
= NULL
;
495 switch (ch
->ifassert_state
) {
496 case PIM_IFASSERT_I_AM_WINNER
:
497 assert_action_a3(ch
);
499 case PIM_IFASSERT_I_AM_LOSER
:
500 assert_action_a5(ch
);
503 if (PIM_DEBUG_PIM_EVENTS
)
505 "%s: (S,G)=%s invalid assert state %d on interface %s",
506 __func__
, ch
->sg_str
, ch
->ifassert_state
,
512 static void assert_timer_off(struct pim_ifchannel
*ch
)
514 if (PIM_DEBUG_PIM_TRACE
) {
515 if (ch
->t_ifassert_timer
) {
517 "%s: (S,G)=%s cancelling timer on interface %s",
518 __func__
, ch
->sg_str
, ch
->interface
->name
);
521 THREAD_OFF(ch
->t_ifassert_timer
);
524 static void pim_assert_timer_set(struct pim_ifchannel
*ch
, int interval
)
526 assert_timer_off(ch
);
528 if (PIM_DEBUG_PIM_TRACE
) {
529 zlog_debug("%s: (S,G)=%s starting %u sec timer on interface %s",
530 __func__
, ch
->sg_str
, interval
, ch
->interface
->name
);
533 thread_add_timer(router
->master
, on_assert_timer
, ch
, interval
,
534 &ch
->t_ifassert_timer
);
537 static void pim_assert_timer_reset(struct pim_ifchannel
*ch
)
539 pim_assert_timer_set(ch
,
540 PIM_ASSERT_TIME
- PIM_ASSERT_OVERRIDE_INTERVAL
);
544 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
546 (S,G) Assert State machine Actions
548 A1: Send Assert(S,G).
549 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
550 Store self as AssertWinner(S,G,I).
551 Store spt_assert_metric(S,I) as AssertWinnerMetric(S,G,I).
553 int assert_action_a1(struct pim_ifchannel
*ch
)
555 struct interface
*ifp
= ch
->interface
;
556 struct pim_interface
*pim_ifp
;
560 zlog_warn("%s: (S,G)=%s multicast not enabled on interface %s",
561 __func__
, ch
->sg_str
, ifp
->name
);
562 return -1; /* must return since pim_ifp is used below */
565 /* Switch to I_AM_WINNER before performing action_a3 below */
566 pim_ifassert_winner_set(
567 ch
, PIM_IFASSERT_I_AM_WINNER
, pim_ifp
->primary_address
,
568 pim_macro_spt_assert_metric(&ch
->upstream
->rpf
,
569 pim_ifp
->primary_address
));
571 if (assert_action_a3(ch
)) {
573 "%s: (S,G)=%s assert_action_a3 failure on interface %s",
574 __func__
, ch
->sg_str
, ifp
->name
);
578 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_WINNER
) {
579 if (PIM_DEBUG_PIM_EVENTS
)
581 "%s: channel%s not in expected PIM_IFASSERT_I_AM_WINNER state",
582 __func__
, ch
->sg_str
);
589 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
591 (S,G) Assert State machine Actions
593 A2: Store new assert winner as AssertWinner(S,G,I) and assert
594 winner metric as AssertWinnerMetric(S,G,I).
595 Set Assert Timer to Assert_Time.
597 static void assert_action_a2(struct pim_ifchannel
*ch
,
598 struct pim_assert_metric winner_metric
)
600 pim_ifassert_winner_set(ch
, PIM_IFASSERT_I_AM_LOSER
,
601 winner_metric
.ip_address
, winner_metric
);
603 pim_assert_timer_set(ch
, PIM_ASSERT_TIME
);
605 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_LOSER
) {
606 if (PIM_DEBUG_PIM_EVENTS
)
608 "%s: channel%s not in expected PIM_IFASSERT_I_AM_LOSER state",
609 __func__
, ch
->sg_str
);
614 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
616 (S,G) Assert State machine Actions
618 A3: Send Assert(S,G).
619 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
621 static int assert_action_a3(struct pim_ifchannel
*ch
)
623 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_WINNER
) {
624 if (PIM_DEBUG_PIM_EVENTS
)
626 "%s: channel%s expected to be in PIM_IFASSERT_I_AM_WINNER state",
627 __func__
, ch
->sg_str
);
631 pim_assert_timer_reset(ch
);
633 if (pim_assert_send(ch
)) {
634 zlog_warn("%s: (S,G)=%s failure sending assert on interface %s",
635 __func__
, ch
->sg_str
, ch
->interface
->name
);
643 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
645 (S,G) Assert State machine Actions
647 A4: Send AssertCancel(S,G).
648 Delete assert info (AssertWinner(S,G,I) and
649 AssertWinnerMetric(S,G,I) will then return their default
652 void assert_action_a4(struct pim_ifchannel
*ch
)
654 if (pim_assert_cancel(ch
)) {
655 zlog_warn("%s: failure sending AssertCancel%s on interface %s",
656 __func__
, ch
->sg_str
, ch
->interface
->name
);
657 /* log warning only */
660 assert_action_a5(ch
);
662 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
663 if (PIM_DEBUG_PIM_EVENTS
)
665 "%s: channel%s not in PIM_IFASSERT_NOINFO state as expected",
666 __func__
, ch
->sg_str
);
671 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
673 (S,G) Assert State machine Actions
675 A5: Delete assert info (AssertWinner(S,G,I) and
676 AssertWinnerMetric(S,G,I) will then return their default values).
678 void assert_action_a5(struct pim_ifchannel
*ch
)
680 reset_ifassert_state(ch
);
681 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
682 if (PIM_DEBUG_PIM_EVENTS
)
684 "%s: channel%s not in PIM_IFSSERT_NOINFO state as expected",
685 __func__
, ch
->sg_str
);
690 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
692 (S,G) Assert State machine Actions
694 A6: Store new assert winner as AssertWinner(S,G,I) and assert
695 winner metric as AssertWinnerMetric(S,G,I).
696 Set Assert Timer to Assert_Time.
697 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true)
698 set SPTbit(S,G) to true.
700 static void assert_action_a6(struct pim_ifchannel
*ch
,
701 struct pim_assert_metric winner_metric
)
703 assert_action_a2(ch
, winner_metric
);
706 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) set
709 if (ch
->upstream
->rpf
.source_nexthop
.interface
== ch
->interface
)
710 if (ch
->upstream
->join_state
== PIM_UPSTREAM_JOINED
)
711 ch
->upstream
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
713 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_LOSER
) {
714 if (PIM_DEBUG_PIM_EVENTS
)
716 "%s: channel%s not in PIM_IFASSERT_I_AM_LOSER state as expected",
717 __func__
, ch
->sg_str
);