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
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,
35 #include "pim_iface.h"
36 #include "pim_hello.h"
37 #include "pim_macro.h"
38 #include "pim_assert.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
,
49 struct in_addr winner
,
50 struct pim_assert_metric winner_metric
)
52 int winner_changed
= (ch
->ifassert_winner
.s_addr
!= winner
.s_addr
);
53 int metric_changed
= !pim_assert_metric_match(&ch
->ifassert_winner_metric
,
56 if (PIM_DEBUG_PIM_EVENTS
) {
57 if (ch
->ifassert_state
!= new_state
) {
58 zlog_debug("%s: (S,G)=%s assert state changed from %s to %s on interface %s",
60 pim_str_sg_dump (&ch
->sg
),
61 pim_ifchannel_ifassert_name(ch
->ifassert_state
),
62 pim_ifchannel_ifassert_name(new_state
),
69 pim_inet4_dump("<was?>", ch
->ifassert_winner
, was_str
, sizeof(was_str
));
70 pim_inet4_dump("<winner?>", winner
, winner_str
, sizeof(winner_str
));
71 zlog_debug("%s: (S,G)=%s assert winner changed from %s to %s on interface %s",
73 pim_str_sg_dump (&ch
->sg
),
74 was_str
, winner_str
, ch
->interface
->name
);
76 } /* PIM_DEBUG_PIM_EVENTS */
78 ch
->ifassert_state
= new_state
;
79 ch
->ifassert_winner
= winner
;
80 ch
->ifassert_winner_metric
= winner_metric
;
81 ch
->ifassert_creation
= pim_time_monotonic_sec();
83 if (winner_changed
|| metric_changed
) {
84 pim_upstream_update_join_desired(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
,
91 struct interface
*ifp
, struct in_addr src
)
93 if (PIM_DEBUG_PIM_TRACE
) {
95 pim_inet4_dump("<src?>", src
, src_str
, sizeof(src_str
));
96 zlog_debug("%s: from %s on %s",
97 label
, src_str
, ifp
->name
);
101 static int preferred_assert(const struct pim_ifchannel
*ch
,
102 const struct pim_assert_metric
*recv_metric
)
104 return pim_assert_metric_better(recv_metric
,
105 &ch
->ifassert_winner_metric
);
108 static int acceptable_assert(const struct pim_assert_metric
*my_metric
,
109 const struct pim_assert_metric
*recv_metric
)
111 return pim_assert_metric_better(recv_metric
,
115 static int inferior_assert(const struct pim_assert_metric
*my_metric
,
116 const struct pim_assert_metric
*recv_metric
)
118 return pim_assert_metric_better(my_metric
,
122 static int cancel_assert(const struct pim_assert_metric
*recv_metric
)
124 return (recv_metric
->metric_preference
== PIM_ASSERT_METRIC_PREFERENCE_MAX
)
126 (recv_metric
->route_metric
== PIM_ASSERT_ROUTE_METRIC_MAX
);
129 static void if_could_assert_do_a1(const char *caller
,
130 struct pim_ifchannel
*ch
)
132 if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
133 if (assert_action_a1(ch
)) {
134 zlog_warn("%s: %s: (S,G)=%s assert_action_a1 failure on interface %s",
135 __PRETTY_FUNCTION__
, caller
,
136 pim_str_sg_dump (&ch
->sg
), ch
->interface
->name
);
137 /* log warning only */
142 static int dispatch_assert(struct interface
*ifp
,
143 struct in_addr source_addr
,
144 struct in_addr group_addr
,
145 struct pim_assert_metric recv_metric
)
147 struct pim_ifchannel
*ch
;
150 memset (&sg
, 0, sizeof (struct prefix_sg
));
151 sg
.src
= source_addr
;
153 ch
= pim_ifchannel_add(ifp
, &sg
);
155 zlog_warn("%s: (S,G)=%s failure creating channel on interface %s",
157 pim_str_sg_dump (&sg
), ifp
->name
);
161 switch (ch
->ifassert_state
) {
162 case PIM_IFASSERT_NOINFO
:
163 if (recv_metric
.rpt_bit_flag
) {
165 if_could_assert_do_a1(__PRETTY_FUNCTION__
, ch
);
169 if (inferior_assert(&ch
->ifassert_my_metric
, &recv_metric
)) {
170 if_could_assert_do_a1(__PRETTY_FUNCTION__
, ch
);
172 else if (acceptable_assert(&ch
->ifassert_my_metric
, &recv_metric
)) {
173 if (PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
)) {
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
);
184 if (inferior_assert(&ch
->ifassert_my_metric
, &recv_metric
)) {
185 zassert(ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
); /* a3 requirement */
186 assert_action_a3(ch
);
190 case PIM_IFASSERT_I_AM_LOSER
:
191 if (recv_metric
.ip_address
.s_addr
== ch
->ifassert_winner
.s_addr
) {
192 /* Assert from current winner */
194 if (cancel_assert(&recv_metric
)) {
195 assert_action_a5(ch
);
198 if (inferior_assert(&ch
->ifassert_my_metric
, &recv_metric
)) {
199 assert_action_a5(ch
);
201 else if (acceptable_assert(&ch
->ifassert_my_metric
, &recv_metric
)) {
202 if (!recv_metric
.rpt_bit_flag
) {
203 assert_action_a2(ch
, recv_metric
);
208 else if (preferred_assert(ch
, &recv_metric
)) {
209 assert_action_a2(ch
, recv_metric
);
214 zlog_warn("%s: (S,G)=%s invalid assert state %d on interface %s",
216 pim_str_sg_dump (&sg
), ch
->ifassert_state
, ifp
->name
);
224 int pim_assert_recv(struct interface
*ifp
,
225 struct pim_neighbor
*neigh
,
226 struct in_addr src_addr
,
227 uint8_t *buf
, int buf_size
)
229 struct prefix msg_group_addr
;
230 struct prefix msg_source_addr
;
231 struct pim_assert_metric msg_metric
;
236 on_trace(__PRETTY_FUNCTION__
, ifp
, src_addr
);
239 curr_size
= buf_size
;
242 Parse assert group addr
244 offset
= pim_parse_addr_group (&msg_group_addr
, curr
, curr_size
);
247 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
248 zlog_warn("%s: pim_parse_addr_group() failure: from %s on %s",
257 Parse assert source addr
259 offset
= pim_parse_addr_ucast (&msg_source_addr
, curr
, curr_size
);
262 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
263 zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
271 if (curr_size
!= 8) {
273 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
274 zlog_warn("%s: preference/metric size is not 8: size=%d from %s on interface %s",
282 Parse assert metric preference
285 msg_metric
.metric_preference
= pim_read_uint32_host(curr
);
287 msg_metric
.rpt_bit_flag
= msg_metric
.metric_preference
& 0x80000000; /* save highest bit */
288 msg_metric
.metric_preference
&= ~0x80000000; /* clear highest bit */
293 Parse assert route metric
296 msg_metric
.route_metric
= pim_read_uint32_host(curr
);
298 if (PIM_DEBUG_PIM_TRACE
) {
300 char source_str
[100];
302 pim_inet4_dump("<neigh?>", src_addr
, neigh_str
, sizeof(neigh_str
));
303 pim_inet4_dump("<src?>", msg_source_addr
.u
.prefix4
, source_str
, sizeof(source_str
));
304 pim_inet4_dump("<grp?>", msg_group_addr
.u
.prefix4
, group_str
, sizeof(group_str
));
305 zlog_debug("%s: from %s on %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
306 __PRETTY_FUNCTION__
, neigh_str
, ifp
->name
,
307 source_str
, group_str
,
308 msg_metric
.metric_preference
,
309 msg_metric
.route_metric
,
310 PIM_FORCE_BOOLEAN(msg_metric
.rpt_bit_flag
));
313 msg_metric
.ip_address
= src_addr
;
315 return dispatch_assert(ifp
,
316 msg_source_addr
.u
.prefix4
,
317 msg_group_addr
.u
.prefix4
,
322 RFC 4601: 4.6.3. Assert Metrics
324 Assert metrics are defined as:
326 When comparing assert_metrics, the rpt_bit_flag, metric_preference,
327 and route_metric field are compared in order, where the first lower
328 value wins. If all fields are equal, the primary IP address of the
329 router that sourced the Assert message is used as a tie-breaker,
330 with the highest IP address winning.
332 int pim_assert_metric_better(const struct pim_assert_metric
*m1
,
333 const struct pim_assert_metric
*m2
)
335 if (m1
->rpt_bit_flag
< m2
->rpt_bit_flag
)
337 if (m1
->rpt_bit_flag
> m2
->rpt_bit_flag
)
340 if (m1
->metric_preference
< m2
->metric_preference
)
342 if (m1
->metric_preference
> m2
->metric_preference
)
345 if (m1
->route_metric
< m2
->route_metric
)
347 if (m1
->route_metric
> m2
->route_metric
)
350 return ntohl(m1
->ip_address
.s_addr
) > ntohl(m2
->ip_address
.s_addr
);
353 int pim_assert_metric_match(const struct pim_assert_metric
*m1
,
354 const struct pim_assert_metric
*m2
)
356 if (m1
->rpt_bit_flag
!= m2
->rpt_bit_flag
)
358 if (m1
->metric_preference
!= m2
->metric_preference
)
360 if (m1
->route_metric
!= m2
->route_metric
)
363 return m1
->ip_address
.s_addr
== m2
->ip_address
.s_addr
;
366 int pim_assert_build_msg(uint8_t *pim_msg
, int buf_size
,
367 struct interface
*ifp
,
368 struct in_addr group_addr
,
369 struct in_addr source_addr
,
370 uint32_t metric_preference
,
371 uint32_t route_metric
,
372 uint32_t rpt_bit_flag
)
374 uint8_t *buf_pastend
= pim_msg
+ buf_size
;
375 uint8_t *pim_msg_curr
;
379 pim_msg_curr
= pim_msg
+ PIM_MSG_HEADER_LEN
; /* skip room for pim header */
382 remain
= buf_pastend
- pim_msg_curr
;
383 pim_msg_curr
= pim_msg_addr_encode_ipv4_group(pim_msg_curr
,
388 pim_inet4_dump("<grp?>", group_addr
, group_str
, sizeof(group_str
));
389 zlog_warn("%s: failure encoding group address %s: space left=%d",
390 __PRETTY_FUNCTION__
, group_str
, remain
);
395 remain
= buf_pastend
- pim_msg_curr
;
396 pim_msg_curr
= pim_msg_addr_encode_ipv4_ucast(pim_msg_curr
,
400 char source_str
[100];
401 pim_inet4_dump("<src?>", source_addr
, source_str
, sizeof(source_str
));
402 zlog_warn("%s: failure encoding source address %s: space left=%d",
403 __PRETTY_FUNCTION__
, source_str
, remain
);
407 /* Metric preference */
408 pim_write_uint32(pim_msg_curr
, rpt_bit_flag
?
409 metric_preference
| 0x80000000 :
414 pim_write_uint32(pim_msg_curr
, route_metric
);
420 pim_msg_size
= pim_msg_curr
- pim_msg
;
421 pim_msg_build_header(pim_msg
, pim_msg_size
,
422 PIM_MSG_TYPE_ASSERT
);
427 static int pim_assert_do(struct pim_ifchannel
*ch
,
428 struct pim_assert_metric metric
)
430 struct interface
*ifp
;
431 struct pim_interface
*pim_ifp
;
432 uint8_t pim_msg
[1000];
440 zlog_warn("%s: pim not enabled on interface: %s",
441 __PRETTY_FUNCTION__
, ifp
->name
);
445 pim_msg_size
= pim_assert_build_msg(pim_msg
, sizeof(pim_msg
), ifp
,
446 ch
->sg
.grp
, ch
->sg
.src
,
447 metric
.metric_preference
,
449 metric
.rpt_bit_flag
);
450 if (pim_msg_size
< 1) {
451 zlog_warn("%s: failure building PIM assert message: msg_size=%d",
452 __PRETTY_FUNCTION__
, pim_msg_size
);
457 RFC 4601: 4.3.1. Sending Hello Messages
459 Thus, if a router needs to send a Join/Prune or Assert message on
460 an interface on which it has not yet sent a Hello message with the
461 currently configured IP address, then it MUST immediately send the
462 relevant Hello message without waiting for the Hello Timer to
463 expire, followed by the Join/Prune or Assert message.
465 pim_hello_require(ifp
);
467 if (PIM_DEBUG_PIM_TRACE
) {
468 zlog_debug("%s: to %s: (S,G)=%s pref=%u metric=%u rpt_bit=%u",
470 ifp
->name
, pim_str_sg_dump (&ch
->sg
),
471 metric
.metric_preference
,
473 PIM_FORCE_BOOLEAN(metric
.rpt_bit_flag
));
476 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
477 pim_ifp
->primary_address
,
478 qpim_all_pim_routers_addr
,
482 zlog_warn("%s: could not send PIM message on interface %s",
483 __PRETTY_FUNCTION__
, ifp
->name
);
490 int pim_assert_send(struct pim_ifchannel
*ch
)
492 return pim_assert_do(ch
, ch
->ifassert_my_metric
);
496 RFC 4601: 4.6.4. AssertCancel Messages
498 An AssertCancel(S,G) is an infinite metric assert with the RPT bit
499 set that names S as the source.
501 static int pim_assert_cancel(struct pim_ifchannel
*ch
)
503 struct pim_assert_metric metric
;
505 metric
.rpt_bit_flag
= 0;
506 metric
.metric_preference
= PIM_ASSERT_METRIC_PREFERENCE_MAX
;
507 metric
.route_metric
= PIM_ASSERT_ROUTE_METRIC_MAX
;
508 metric
.ip_address
= ch
->sg
.src
;
510 return pim_assert_do(ch
, metric
);
513 static int on_assert_timer(struct thread
*t
)
515 struct pim_ifchannel
*ch
;
516 struct interface
*ifp
;
525 if (PIM_DEBUG_PIM_TRACE
) {
526 zlog_debug("%s: (S,G)=%s timer expired on interface %s",
528 pim_str_sg_dump (&ch
->sg
), ifp
->name
);
531 ch
->t_ifassert_timer
= NULL
;
533 switch (ch
->ifassert_state
) {
534 case PIM_IFASSERT_I_AM_WINNER
:
535 zassert(ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
); /* a3 requirement */
536 assert_action_a3(ch
);
538 case PIM_IFASSERT_I_AM_LOSER
:
539 assert_action_a5(ch
);
543 zlog_warn("%s: (S,G)=%s invalid assert state %d on interface %s",
545 pim_str_sg_dump (&ch
->sg
), ch
->ifassert_state
, ifp
->name
);
552 static void assert_timer_off(struct pim_ifchannel
*ch
)
554 struct interface
*ifp
;
560 if (PIM_DEBUG_PIM_TRACE
) {
561 if (ch
->t_ifassert_timer
) {
562 zlog_debug("%s: (S,G)=%s cancelling timer on interface %s",
564 pim_str_sg_dump (&ch
->sg
), ifp
->name
);
567 THREAD_OFF(ch
->t_ifassert_timer
);
568 zassert(!ch
->t_ifassert_timer
);
571 static void pim_assert_timer_set(struct pim_ifchannel
*ch
,
574 struct interface
*ifp
;
580 assert_timer_off(ch
);
582 if (PIM_DEBUG_PIM_TRACE
) {
583 zlog_debug("%s: (S,G)=%s starting %u sec timer on interface %s",
585 pim_str_sg_dump(&ch
->sg
), interval
, ifp
->name
);
588 THREAD_TIMER_ON(master
, ch
->t_ifassert_timer
,
593 static void pim_assert_timer_reset(struct pim_ifchannel
*ch
)
595 pim_assert_timer_set(ch
, PIM_ASSERT_TIME
- PIM_ASSERT_OVERRIDE_INTERVAL
);
599 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
601 (S,G) Assert State machine Actions
603 A1: Send Assert(S,G).
604 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
605 Store self as AssertWinner(S,G,I).
606 Store spt_assert_metric(S,I) as AssertWinnerMetric(S,G,I).
608 int assert_action_a1(struct pim_ifchannel
*ch
)
610 struct interface
*ifp
= ch
->interface
;
611 struct pim_interface
*pim_ifp
;
617 zlog_warn("%s: (S,G)=%s multicast not enabled on interface %s",
619 pim_str_sg_dump (&ch
->sg
), ifp
->name
);
620 return -1; /* must return since pim_ifp is used below */
623 /* Switch to I_AM_WINNER before performing action_a3 below */
624 pim_ifassert_winner_set(ch
, PIM_IFASSERT_I_AM_WINNER
,
625 pim_ifp
->primary_address
,
626 pim_macro_spt_assert_metric(&ch
->upstream
->rpf
,
627 pim_ifp
->primary_address
));
629 zassert(ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
); /* a3 requirement */
630 if (assert_action_a3(ch
)) {
631 zlog_warn("%s: (S,G)=%s assert_action_a3 failure on interface %s",
633 pim_str_sg_dump (&ch
->sg
), ifp
->name
);
637 zassert(ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
);
643 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
645 (S,G) Assert State machine Actions
647 A2: Store new assert winner as AssertWinner(S,G,I) and assert
648 winner metric as AssertWinnerMetric(S,G,I).
649 Set Assert Timer to Assert_Time.
651 static void assert_action_a2(struct pim_ifchannel
*ch
,
652 struct pim_assert_metric winner_metric
)
654 pim_ifassert_winner_set(ch
, PIM_IFASSERT_I_AM_LOSER
,
655 winner_metric
.ip_address
,
658 pim_assert_timer_set(ch
, PIM_ASSERT_TIME
);
660 zassert(ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
);
664 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
666 (S,G) Assert State machine Actions
668 A3: Send Assert(S,G).
669 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
671 static int assert_action_a3(struct pim_ifchannel
*ch
)
673 zassert(ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
);
675 pim_assert_timer_reset(ch
);
677 if (pim_assert_send(ch
)) {
678 zlog_warn("%s: (S,G)=%s failure sending assert on interface %s",
680 pim_str_sg_dump (&ch
->sg
), ch
->interface
->name
);
684 zassert(ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
);
690 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
692 (S,G) Assert State machine Actions
694 A4: Send AssertCancel(S,G).
695 Delete assert info (AssertWinner(S,G,I) and
696 AssertWinnerMetric(S,G,I) will then return their default
699 void assert_action_a4(struct pim_ifchannel
*ch
)
701 if (pim_assert_cancel(ch
)) {
702 zlog_warn("%s: failure sending AssertCancel%s on interface %s",
704 pim_str_sg_dump (&ch
->sg
), ch
->interface
->name
);
705 /* log warning only */
708 assert_action_a5(ch
);
710 zassert(ch
->ifassert_state
== PIM_IFASSERT_NOINFO
);
714 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
716 (S,G) Assert State machine Actions
718 A5: Delete assert info (AssertWinner(S,G,I) and
719 AssertWinnerMetric(S,G,I) will then return their default values).
721 void assert_action_a5(struct pim_ifchannel
*ch
)
723 reset_ifassert_state(ch
);
724 zassert(ch
->ifassert_state
== PIM_IFASSERT_NOINFO
);
728 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
730 (S,G) Assert State machine Actions
732 A6: Store new assert winner as AssertWinner(S,G,I) and assert
733 winner metric as AssertWinnerMetric(S,G,I).
734 Set Assert Timer to Assert_Time.
735 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true)
736 set SPTbit(S,G) to TRUE.
738 static void assert_action_a6(struct pim_ifchannel
*ch
,
739 struct pim_assert_metric winner_metric
)
741 assert_action_a2(ch
, winner_metric
);
744 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) set
747 if (ch
->upstream
->rpf
.source_nexthop
.interface
== ch
->interface
)
748 if (ch
->upstream
->join_state
== PIM_UPSTREAM_JOINED
)
749 ch
->upstream
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
751 zassert(ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
);