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 uint8_t *buf_pastend
= pim_msg
+ buf_size
;
342 uint8_t *pim_msg_curr
;
347 pim_msg
+ PIM_MSG_HEADER_LEN
; /* skip room for pim header */
350 remain
= buf_pastend
- pim_msg_curr
;
351 pim_msg_curr
= pim_msg_addr_encode_group(pim_msg_curr
, group_addr
);
354 "%s: failure encoding group address %pPA: space left=%d",
355 __func__
, &group_addr
, remain
);
360 remain
= buf_pastend
- pim_msg_curr
;
361 pim_msg_curr
= pim_msg_addr_encode_ucast(pim_msg_curr
, source_addr
);
364 "%s: failure encoding source address %pPA: space left=%d",
365 __func__
, &source_addr
, remain
);
369 /* Metric preference */
370 pim_write_uint32(pim_msg_curr
,
371 rpt_bit_flag
? metric_preference
| 0x80000000
372 : metric_preference
);
376 pim_write_uint32(pim_msg_curr
, route_metric
);
382 pim_msg_size
= pim_msg_curr
- pim_msg
;
383 pim_msg_build_header(pim_msg
, pim_msg_size
, PIM_MSG_TYPE_ASSERT
, false);
388 static int pim_assert_do(struct pim_ifchannel
*ch
,
389 struct pim_assert_metric metric
)
391 struct interface
*ifp
;
392 struct pim_interface
*pim_ifp
;
393 uint8_t pim_msg
[1000];
398 if (PIM_DEBUG_PIM_TRACE
)
399 zlog_debug("%s: channel%s has no associated interface!",
400 __func__
, ch
->sg_str
);
405 if (PIM_DEBUG_PIM_TRACE
)
407 "%s: channel %s pim not enabled on interface: %s",
408 __func__
, ch
->sg_str
, ifp
->name
);
413 pim_assert_build_msg(pim_msg
, sizeof(pim_msg
), ifp
, ch
->sg
.grp
,
414 ch
->sg
.src
, metric
.metric_preference
,
415 metric
.route_metric
, metric
.rpt_bit_flag
);
416 if (pim_msg_size
< 1) {
418 "%s: failure building PIM assert message: msg_size=%d",
419 __func__
, pim_msg_size
);
424 RFC 4601: 4.3.1. Sending Hello Messages
426 Thus, if a router needs to send a Join/Prune or Assert message on
427 an interface on which it has not yet sent a Hello message with the
428 currently configured IP address, then it MUST immediately send the
429 relevant Hello message without waiting for the Hello Timer to
430 expire, followed by the Join/Prune or Assert message.
432 pim_hello_require(ifp
);
434 if (PIM_DEBUG_PIM_TRACE
) {
435 zlog_debug("%s: to %s: (S,G)=%s pref=%u metric=%u rpt_bit=%u",
436 __func__
, ifp
->name
, ch
->sg_str
,
437 metric
.metric_preference
, metric
.route_metric
,
438 PIM_FORCE_BOOLEAN(metric
.rpt_bit_flag
));
440 ++pim_ifp
->pim_ifstat_assert_send
;
442 if (pim_msg_send(pim_ifp
->pim_sock_fd
, pim_ifp
->primary_address
,
443 qpim_all_pim_routers_addr
, pim_msg
, pim_msg_size
,
445 zlog_warn("%s: could not send PIM message on interface %s",
446 __func__
, ifp
->name
);
453 int pim_assert_send(struct pim_ifchannel
*ch
)
455 return pim_assert_do(ch
, ch
->ifassert_my_metric
);
459 RFC 4601: 4.6.4. AssertCancel Messages
461 An AssertCancel(S,G) is an infinite metric assert with the RPT bit
462 set that names S as the source.
464 static int pim_assert_cancel(struct pim_ifchannel
*ch
)
466 struct pim_assert_metric metric
;
468 metric
.rpt_bit_flag
= 0;
469 metric
.metric_preference
= PIM_ASSERT_METRIC_PREFERENCE_MAX
;
470 metric
.route_metric
= PIM_ASSERT_ROUTE_METRIC_MAX
;
471 metric
.ip_address
= ch
->sg
.src
;
473 return pim_assert_do(ch
, metric
);
476 static void on_assert_timer(struct thread
*t
)
478 struct pim_ifchannel
*ch
;
479 struct interface
*ifp
;
485 if (PIM_DEBUG_PIM_TRACE
) {
486 zlog_debug("%s: (S,G)=%s timer expired on interface %s",
487 __func__
, ch
->sg_str
, ifp
->name
);
490 ch
->t_ifassert_timer
= NULL
;
492 switch (ch
->ifassert_state
) {
493 case PIM_IFASSERT_I_AM_WINNER
:
494 assert_action_a3(ch
);
496 case PIM_IFASSERT_I_AM_LOSER
:
497 assert_action_a5(ch
);
500 if (PIM_DEBUG_PIM_EVENTS
)
502 "%s: (S,G)=%s invalid assert state %d on interface %s",
503 __func__
, ch
->sg_str
, ch
->ifassert_state
,
509 static void assert_timer_off(struct pim_ifchannel
*ch
)
511 if (PIM_DEBUG_PIM_TRACE
) {
512 if (ch
->t_ifassert_timer
) {
514 "%s: (S,G)=%s cancelling timer on interface %s",
515 __func__
, ch
->sg_str
, ch
->interface
->name
);
518 THREAD_OFF(ch
->t_ifassert_timer
);
521 static void pim_assert_timer_set(struct pim_ifchannel
*ch
, int interval
)
523 assert_timer_off(ch
);
525 if (PIM_DEBUG_PIM_TRACE
) {
526 zlog_debug("%s: (S,G)=%s starting %u sec timer on interface %s",
527 __func__
, ch
->sg_str
, interval
, ch
->interface
->name
);
530 thread_add_timer(router
->master
, on_assert_timer
, ch
, interval
,
531 &ch
->t_ifassert_timer
);
534 static void pim_assert_timer_reset(struct pim_ifchannel
*ch
)
536 pim_assert_timer_set(ch
,
537 PIM_ASSERT_TIME
- PIM_ASSERT_OVERRIDE_INTERVAL
);
541 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
543 (S,G) Assert State machine Actions
545 A1: Send Assert(S,G).
546 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
547 Store self as AssertWinner(S,G,I).
548 Store spt_assert_metric(S,I) as AssertWinnerMetric(S,G,I).
550 int assert_action_a1(struct pim_ifchannel
*ch
)
552 struct interface
*ifp
= ch
->interface
;
553 struct pim_interface
*pim_ifp
;
557 zlog_warn("%s: (S,G)=%s multicast not enabled on interface %s",
558 __func__
, ch
->sg_str
, ifp
->name
);
559 return -1; /* must return since pim_ifp is used below */
562 /* Switch to I_AM_WINNER before performing action_a3 below */
563 pim_ifassert_winner_set(
564 ch
, PIM_IFASSERT_I_AM_WINNER
, pim_ifp
->primary_address
,
565 pim_macro_spt_assert_metric(&ch
->upstream
->rpf
,
566 pim_ifp
->primary_address
));
568 if (assert_action_a3(ch
)) {
570 "%s: (S,G)=%s assert_action_a3 failure on interface %s",
571 __func__
, ch
->sg_str
, ifp
->name
);
575 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_WINNER
) {
576 if (PIM_DEBUG_PIM_EVENTS
)
578 "%s: channel%s not in expected PIM_IFASSERT_I_AM_WINNER state",
579 __func__
, ch
->sg_str
);
586 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
588 (S,G) Assert State machine Actions
590 A2: Store new assert winner as AssertWinner(S,G,I) and assert
591 winner metric as AssertWinnerMetric(S,G,I).
592 Set Assert Timer to Assert_Time.
594 static void assert_action_a2(struct pim_ifchannel
*ch
,
595 struct pim_assert_metric winner_metric
)
597 pim_ifassert_winner_set(ch
, PIM_IFASSERT_I_AM_LOSER
,
598 winner_metric
.ip_address
, winner_metric
);
600 pim_assert_timer_set(ch
, PIM_ASSERT_TIME
);
602 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_LOSER
) {
603 if (PIM_DEBUG_PIM_EVENTS
)
605 "%s: channel%s not in expected PIM_IFASSERT_I_AM_LOSER state",
606 __func__
, ch
->sg_str
);
611 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
613 (S,G) Assert State machine Actions
615 A3: Send Assert(S,G).
616 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
618 static int assert_action_a3(struct pim_ifchannel
*ch
)
620 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_WINNER
) {
621 if (PIM_DEBUG_PIM_EVENTS
)
623 "%s: channel%s expected to be in PIM_IFASSERT_I_AM_WINNER state",
624 __func__
, ch
->sg_str
);
628 pim_assert_timer_reset(ch
);
630 if (pim_assert_send(ch
)) {
631 zlog_warn("%s: (S,G)=%s failure sending assert on interface %s",
632 __func__
, ch
->sg_str
, ch
->interface
->name
);
640 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
642 (S,G) Assert State machine Actions
644 A4: Send AssertCancel(S,G).
645 Delete assert info (AssertWinner(S,G,I) and
646 AssertWinnerMetric(S,G,I) will then return their default
649 void assert_action_a4(struct pim_ifchannel
*ch
)
651 if (pim_assert_cancel(ch
)) {
652 zlog_warn("%s: failure sending AssertCancel%s on interface %s",
653 __func__
, ch
->sg_str
, ch
->interface
->name
);
654 /* log warning only */
657 assert_action_a5(ch
);
659 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
660 if (PIM_DEBUG_PIM_EVENTS
)
662 "%s: channel%s not in PIM_IFASSERT_NOINFO state as expected",
663 __func__
, ch
->sg_str
);
668 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
670 (S,G) Assert State machine Actions
672 A5: Delete assert info (AssertWinner(S,G,I) and
673 AssertWinnerMetric(S,G,I) will then return their default values).
675 void assert_action_a5(struct pim_ifchannel
*ch
)
677 reset_ifassert_state(ch
);
678 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
679 if (PIM_DEBUG_PIM_EVENTS
)
681 "%s: channel%s not in PIM_IFSSERT_NOINFO state as expected",
682 __func__
, ch
->sg_str
);
687 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
689 (S,G) Assert State machine Actions
691 A6: Store new assert winner as AssertWinner(S,G,I) and assert
692 winner metric as AssertWinnerMetric(S,G,I).
693 Set Assert Timer to Assert_Time.
694 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true)
695 set SPTbit(S,G) to true.
697 static void assert_action_a6(struct pim_ifchannel
*ch
,
698 struct pim_assert_metric winner_metric
)
700 assert_action_a2(ch
, winner_metric
);
703 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) set
706 if (ch
->upstream
->rpf
.source_nexthop
.interface
== ch
->interface
)
707 if (ch
->upstream
->join_state
== PIM_UPSTREAM_JOINED
)
708 ch
->upstream
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
710 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_LOSER
) {
711 if (PIM_DEBUG_PIM_EVENTS
)
713 "%s: channel%s not in PIM_IFASSERT_I_AM_LOSER state as expected",
714 __func__
, ch
->sg_str
);