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
,
47 struct in_addr winner
,
48 struct pim_assert_metric winner_metric
)
50 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
51 int winner_changed
= (ch
->ifassert_winner
.s_addr
!= winner
.s_addr
);
52 int metric_changed
= !pim_assert_metric_match(
53 &ch
->ifassert_winner_metric
, &winner_metric
);
55 if (PIM_DEBUG_PIM_EVENTS
) {
56 if (ch
->ifassert_state
!= new_state
) {
58 "%s: (S,G)=%s assert state changed from %s to %s on interface %s",
60 pim_ifchannel_ifassert_name(ch
->ifassert_state
),
61 pim_ifchannel_ifassert_name(new_state
),
66 char was_str
[INET_ADDRSTRLEN
];
67 char winner_str
[INET_ADDRSTRLEN
];
68 pim_inet4_dump("<was?>", ch
->ifassert_winner
, was_str
,
70 pim_inet4_dump("<winner?>", winner
, winner_str
,
73 "%s: (S,G)=%s assert winner changed from %s to %s on interface %s",
74 __func__
, ch
->sg_str
, was_str
, winner_str
,
77 } /* PIM_DEBUG_PIM_EVENTS */
79 ch
->ifassert_state
= new_state
;
80 ch
->ifassert_winner
= winner
;
81 ch
->ifassert_winner_metric
= winner_metric
;
82 ch
->ifassert_creation
= pim_time_monotonic_sec();
84 if (winner_changed
|| metric_changed
) {
85 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
86 pim_ifchannel_update_could_assert(ch
);
87 pim_ifchannel_update_assert_tracking_desired(ch
);
91 static void on_trace(const char *label
, struct interface
*ifp
,
94 if (PIM_DEBUG_PIM_TRACE
) {
95 char src_str
[INET_ADDRSTRLEN
];
96 pim_inet4_dump("<src?>", src
, src_str
, sizeof(src_str
));
97 zlog_debug("%s: from %s on %s", 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
, my_metric
);
114 static int inferior_assert(const struct pim_assert_metric
*my_metric
,
115 const struct pim_assert_metric
*recv_metric
)
117 return pim_assert_metric_better(my_metric
, recv_metric
);
120 static int cancel_assert(const struct pim_assert_metric
*recv_metric
)
122 return (recv_metric
->metric_preference
123 == PIM_ASSERT_METRIC_PREFERENCE_MAX
)
124 && (recv_metric
->route_metric
== PIM_ASSERT_ROUTE_METRIC_MAX
);
127 static void if_could_assert_do_a1(const char *caller
, struct pim_ifchannel
*ch
)
129 if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
130 if (assert_action_a1(ch
)) {
132 "%s: %s: (S,G)=%s assert_action_a1 failure on interface %s",
133 __func__
, caller
, ch
->sg_str
,
134 ch
->interface
->name
);
135 /* log warning only */
140 static int dispatch_assert(struct interface
*ifp
, struct in_addr source_addr
,
141 struct in_addr group_addr
,
142 struct pim_assert_metric recv_metric
)
144 struct pim_ifchannel
*ch
;
147 memset(&sg
, 0, sizeof(struct prefix_sg
));
148 sg
.src
= source_addr
;
150 ch
= pim_ifchannel_add(ifp
, &sg
, 0, 0);
152 switch (ch
->ifassert_state
) {
153 case PIM_IFASSERT_NOINFO
:
154 if (recv_metric
.rpt_bit_flag
) {
156 if_could_assert_do_a1(__func__
, ch
);
159 if (inferior_assert(&ch
->ifassert_my_metric
,
161 if_could_assert_do_a1(__func__
, ch
);
162 } else if (acceptable_assert(&ch
->ifassert_my_metric
,
164 if (PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(
166 assert_action_a6(ch
, recv_metric
);
171 case PIM_IFASSERT_I_AM_WINNER
:
172 if (preferred_assert(ch
, &recv_metric
)) {
173 assert_action_a2(ch
, recv_metric
);
175 if (inferior_assert(&ch
->ifassert_my_metric
,
177 assert_action_a3(ch
);
181 case PIM_IFASSERT_I_AM_LOSER
:
182 if (recv_metric
.ip_address
.s_addr
183 == ch
->ifassert_winner
.s_addr
) {
184 /* Assert from current winner */
186 if (cancel_assert(&recv_metric
)) {
187 assert_action_a5(ch
);
189 if (inferior_assert(&ch
->ifassert_my_metric
,
191 assert_action_a5(ch
);
192 } else if (acceptable_assert(
193 &ch
->ifassert_my_metric
,
195 if (!recv_metric
.rpt_bit_flag
) {
201 } else if (preferred_assert(ch
, &recv_metric
)) {
202 assert_action_a2(ch
, recv_metric
);
207 "%s: (S,G)=%s invalid assert state %d on interface %s",
208 __func__
, ch
->sg_str
, ch
->ifassert_state
, ifp
->name
);
216 int pim_assert_recv(struct interface
*ifp
, struct pim_neighbor
*neigh
,
217 struct in_addr src_addr
, uint8_t *buf
, int buf_size
)
220 struct prefix msg_source_addr
;
221 struct pim_assert_metric msg_metric
;
225 struct pim_interface
*pim_ifp
= NULL
;
227 on_trace(__func__
, ifp
, src_addr
);
230 curr_size
= buf_size
;
233 Parse assert group addr
235 memset(&sg
, 0, sizeof(struct prefix_sg
));
236 offset
= pim_parse_addr_group(&sg
, curr
, curr_size
);
238 char src_str
[INET_ADDRSTRLEN
];
239 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
240 zlog_warn("%s: pim_parse_addr_group() failure: from %s on %s",
241 __func__
, src_str
, ifp
->name
);
248 Parse assert source addr
250 offset
= pim_parse_addr_ucast(&msg_source_addr
, curr
, curr_size
);
252 char src_str
[INET_ADDRSTRLEN
];
253 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
254 zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
255 __func__
, src_str
, ifp
->name
);
262 char src_str
[INET_ADDRSTRLEN
];
263 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
265 "%s: preference/metric size is less than 8 bytes: size=%d from %s on interface %s",
266 __func__
, curr_size
, src_str
, ifp
->name
);
271 Parse assert metric preference
274 msg_metric
.metric_preference
= pim_read_uint32_host(curr
);
276 msg_metric
.rpt_bit_flag
= msg_metric
.metric_preference
277 & 0x80000000; /* save highest bit */
278 msg_metric
.metric_preference
&= ~0x80000000; /* clear highest bit */
283 Parse assert route metric
286 msg_metric
.route_metric
= pim_read_uint32_host(curr
);
288 if (PIM_DEBUG_PIM_TRACE
) {
289 char neigh_str
[INET_ADDRSTRLEN
];
290 char source_str
[INET_ADDRSTRLEN
];
291 char group_str
[INET_ADDRSTRLEN
];
292 pim_inet4_dump("<neigh?>", src_addr
, neigh_str
,
294 pim_inet4_dump("<src?>", msg_source_addr
.u
.prefix4
, source_str
,
296 pim_inet4_dump("<grp?>", sg
.grp
, group_str
, sizeof(group_str
));
298 "%s: from %s on %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
299 __func__
, neigh_str
, ifp
->name
, source_str
, group_str
,
300 msg_metric
.metric_preference
, msg_metric
.route_metric
,
301 PIM_FORCE_BOOLEAN(msg_metric
.rpt_bit_flag
));
304 msg_metric
.ip_address
= src_addr
;
308 ++pim_ifp
->pim_ifstat_assert_recv
;
310 return dispatch_assert(ifp
, msg_source_addr
.u
.prefix4
, sg
.grp
,
315 RFC 4601: 4.6.3. Assert Metrics
317 Assert metrics are defined as:
319 When comparing assert_metrics, the rpt_bit_flag, metric_preference,
320 and route_metric field are compared in order, where the first lower
321 value wins. If all fields are equal, the primary IP address of the
322 router that sourced the Assert message is used as a tie-breaker,
323 with the highest IP address winning.
325 int pim_assert_metric_better(const struct pim_assert_metric
*m1
,
326 const struct pim_assert_metric
*m2
)
328 if (m1
->rpt_bit_flag
< m2
->rpt_bit_flag
)
330 if (m1
->rpt_bit_flag
> m2
->rpt_bit_flag
)
333 if (m1
->metric_preference
< m2
->metric_preference
)
335 if (m1
->metric_preference
> m2
->metric_preference
)
338 if (m1
->route_metric
< m2
->route_metric
)
340 if (m1
->route_metric
> m2
->route_metric
)
343 return ntohl(m1
->ip_address
.s_addr
) > ntohl(m2
->ip_address
.s_addr
);
346 int pim_assert_metric_match(const struct pim_assert_metric
*m1
,
347 const struct pim_assert_metric
*m2
)
349 if (m1
->rpt_bit_flag
!= m2
->rpt_bit_flag
)
351 if (m1
->metric_preference
!= m2
->metric_preference
)
353 if (m1
->route_metric
!= m2
->route_metric
)
356 return m1
->ip_address
.s_addr
== m2
->ip_address
.s_addr
;
359 int pim_assert_build_msg(uint8_t *pim_msg
, int buf_size
, struct interface
*ifp
,
360 struct in_addr group_addr
, struct in_addr source_addr
,
361 uint32_t metric_preference
, uint32_t route_metric
,
362 uint32_t rpt_bit_flag
)
364 uint8_t *buf_pastend
= pim_msg
+ buf_size
;
365 uint8_t *pim_msg_curr
;
370 pim_msg
+ PIM_MSG_HEADER_LEN
; /* skip room for pim header */
373 remain
= buf_pastend
- pim_msg_curr
;
374 pim_msg_curr
= pim_msg_addr_encode_ipv4_group(pim_msg_curr
, group_addr
);
376 char group_str
[INET_ADDRSTRLEN
];
377 pim_inet4_dump("<grp?>", group_addr
, group_str
,
380 "%s: failure encoding group address %s: space left=%d",
381 __func__
, group_str
, remain
);
386 remain
= buf_pastend
- pim_msg_curr
;
388 pim_msg_addr_encode_ipv4_ucast(pim_msg_curr
, source_addr
);
390 char source_str
[INET_ADDRSTRLEN
];
391 pim_inet4_dump("<src?>", source_addr
, source_str
,
394 "%s: failure encoding source address %s: space left=%d",
395 __func__
, source_str
, 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_msg
, pim_msg_size
, PIM_MSG_TYPE_ASSERT
, false);
418 static int pim_assert_do(struct pim_ifchannel
*ch
,
419 struct pim_assert_metric metric
)
421 struct interface
*ifp
;
422 struct pim_interface
*pim_ifp
;
423 uint8_t pim_msg
[1000];
428 if (PIM_DEBUG_PIM_TRACE
)
429 zlog_debug("%s: channel%s has no associated interface!",
430 __func__
, ch
->sg_str
);
435 if (PIM_DEBUG_PIM_TRACE
)
437 "%s: channel %s pim not enabled on interface: %s",
438 __func__
, ch
->sg_str
, ifp
->name
);
443 pim_assert_build_msg(pim_msg
, sizeof(pim_msg
), ifp
, ch
->sg
.grp
,
444 ch
->sg
.src
, metric
.metric_preference
,
445 metric
.route_metric
, metric
.rpt_bit_flag
);
446 if (pim_msg_size
< 1) {
448 "%s: failure building PIM assert message: msg_size=%d",
449 __func__
, pim_msg_size
);
454 RFC 4601: 4.3.1. Sending Hello Messages
456 Thus, if a router needs to send a Join/Prune or Assert message on
457 an interface on which it has not yet sent a Hello message with the
458 currently configured IP address, then it MUST immediately send the
459 relevant Hello message without waiting for the Hello Timer to
460 expire, followed by the Join/Prune or Assert message.
462 pim_hello_require(ifp
);
464 if (PIM_DEBUG_PIM_TRACE
) {
465 zlog_debug("%s: to %s: (S,G)=%s pref=%u metric=%u rpt_bit=%u",
466 __func__
, ifp
->name
, ch
->sg_str
,
467 metric
.metric_preference
, metric
.route_metric
,
468 PIM_FORCE_BOOLEAN(metric
.rpt_bit_flag
));
470 ++pim_ifp
->pim_ifstat_assert_send
;
472 if (pim_msg_send(pim_ifp
->pim_sock_fd
, pim_ifp
->primary_address
,
473 qpim_all_pim_routers_addr
, pim_msg
, pim_msg_size
,
475 zlog_warn("%s: could not send PIM message on interface %s",
476 __func__
, ifp
->name
);
483 int pim_assert_send(struct pim_ifchannel
*ch
)
485 return pim_assert_do(ch
, ch
->ifassert_my_metric
);
489 RFC 4601: 4.6.4. AssertCancel Messages
491 An AssertCancel(S,G) is an infinite metric assert with the RPT bit
492 set that names S as the source.
494 static int pim_assert_cancel(struct pim_ifchannel
*ch
)
496 struct pim_assert_metric metric
;
498 metric
.rpt_bit_flag
= 0;
499 metric
.metric_preference
= PIM_ASSERT_METRIC_PREFERENCE_MAX
;
500 metric
.route_metric
= PIM_ASSERT_ROUTE_METRIC_MAX
;
501 metric
.ip_address
= ch
->sg
.src
;
503 return pim_assert_do(ch
, metric
);
506 static int on_assert_timer(struct thread
*t
)
508 struct pim_ifchannel
*ch
;
509 struct interface
*ifp
;
515 if (PIM_DEBUG_PIM_TRACE
) {
516 zlog_debug("%s: (S,G)=%s timer expired on interface %s",
517 __func__
, ch
->sg_str
, ifp
->name
);
520 ch
->t_ifassert_timer
= NULL
;
522 switch (ch
->ifassert_state
) {
523 case PIM_IFASSERT_I_AM_WINNER
:
524 assert_action_a3(ch
);
526 case PIM_IFASSERT_I_AM_LOSER
:
527 assert_action_a5(ch
);
530 if (PIM_DEBUG_PIM_EVENTS
)
532 "%s: (S,G)=%s invalid assert state %d on interface %s",
533 __func__
, ch
->sg_str
, ch
->ifassert_state
,
541 static void assert_timer_off(struct pim_ifchannel
*ch
)
543 if (PIM_DEBUG_PIM_TRACE
) {
544 if (ch
->t_ifassert_timer
) {
546 "%s: (S,G)=%s cancelling timer on interface %s",
547 __func__
, ch
->sg_str
, ch
->interface
->name
);
550 THREAD_OFF(ch
->t_ifassert_timer
);
553 static void pim_assert_timer_set(struct pim_ifchannel
*ch
, int interval
)
555 assert_timer_off(ch
);
557 if (PIM_DEBUG_PIM_TRACE
) {
558 zlog_debug("%s: (S,G)=%s starting %u sec timer on interface %s",
559 __func__
, ch
->sg_str
, interval
, ch
->interface
->name
);
562 thread_add_timer(router
->master
, on_assert_timer
, ch
, interval
,
563 &ch
->t_ifassert_timer
);
566 static void pim_assert_timer_reset(struct pim_ifchannel
*ch
)
568 pim_assert_timer_set(ch
,
569 PIM_ASSERT_TIME
- PIM_ASSERT_OVERRIDE_INTERVAL
);
573 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
575 (S,G) Assert State machine Actions
577 A1: Send Assert(S,G).
578 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
579 Store self as AssertWinner(S,G,I).
580 Store spt_assert_metric(S,I) as AssertWinnerMetric(S,G,I).
582 int assert_action_a1(struct pim_ifchannel
*ch
)
584 struct interface
*ifp
= ch
->interface
;
585 struct pim_interface
*pim_ifp
;
589 zlog_warn("%s: (S,G)=%s multicast not enabled on interface %s",
590 __func__
, ch
->sg_str
, ifp
->name
);
591 return -1; /* must return since pim_ifp is used below */
594 /* Switch to I_AM_WINNER before performing action_a3 below */
595 pim_ifassert_winner_set(
596 ch
, PIM_IFASSERT_I_AM_WINNER
, pim_ifp
->primary_address
,
597 pim_macro_spt_assert_metric(&ch
->upstream
->rpf
,
598 pim_ifp
->primary_address
));
600 if (assert_action_a3(ch
)) {
602 "%s: (S,G)=%s assert_action_a3 failure on interface %s",
603 __func__
, ch
->sg_str
, ifp
->name
);
607 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_WINNER
) {
608 if (PIM_DEBUG_PIM_EVENTS
)
610 "%s: channel%s not in expected PIM_IFASSERT_I_AM_WINNER state",
611 __func__
, ch
->sg_str
);
618 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
620 (S,G) Assert State machine Actions
622 A2: Store new assert winner as AssertWinner(S,G,I) and assert
623 winner metric as AssertWinnerMetric(S,G,I).
624 Set Assert Timer to Assert_Time.
626 static void assert_action_a2(struct pim_ifchannel
*ch
,
627 struct pim_assert_metric winner_metric
)
629 pim_ifassert_winner_set(ch
, PIM_IFASSERT_I_AM_LOSER
,
630 winner_metric
.ip_address
, winner_metric
);
632 pim_assert_timer_set(ch
, PIM_ASSERT_TIME
);
634 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_LOSER
) {
635 if (PIM_DEBUG_PIM_EVENTS
)
637 "%s: channel%s not in expected PIM_IFASSERT_I_AM_LOSER state",
638 __func__
, ch
->sg_str
);
643 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
645 (S,G) Assert State machine Actions
647 A3: Send Assert(S,G).
648 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
650 static int assert_action_a3(struct pim_ifchannel
*ch
)
652 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_WINNER
) {
653 if (PIM_DEBUG_PIM_EVENTS
)
655 "%s: channel%s expected to be in PIM_IFASSERT_I_AM_WINNER state",
656 __func__
, ch
->sg_str
);
660 pim_assert_timer_reset(ch
);
662 if (pim_assert_send(ch
)) {
663 zlog_warn("%s: (S,G)=%s failure sending assert on interface %s",
664 __func__
, ch
->sg_str
, ch
->interface
->name
);
672 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
674 (S,G) Assert State machine Actions
676 A4: Send AssertCancel(S,G).
677 Delete assert info (AssertWinner(S,G,I) and
678 AssertWinnerMetric(S,G,I) will then return their default
681 void assert_action_a4(struct pim_ifchannel
*ch
)
683 if (pim_assert_cancel(ch
)) {
684 zlog_warn("%s: failure sending AssertCancel%s on interface %s",
685 __func__
, ch
->sg_str
, ch
->interface
->name
);
686 /* log warning only */
689 assert_action_a5(ch
);
691 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
692 if (PIM_DEBUG_PIM_EVENTS
)
694 "%s: channel%s not in PIM_IFASSERT_NOINFO state as expected",
695 __func__
, ch
->sg_str
);
700 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
702 (S,G) Assert State machine Actions
704 A5: Delete assert info (AssertWinner(S,G,I) and
705 AssertWinnerMetric(S,G,I) will then return their default values).
707 void assert_action_a5(struct pim_ifchannel
*ch
)
709 reset_ifassert_state(ch
);
710 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
711 if (PIM_DEBUG_PIM_EVENTS
)
713 "%s: channel%s not in PIM_IFSSERT_NOINFO state as expected",
714 __func__
, ch
->sg_str
);
719 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
721 (S,G) Assert State machine Actions
723 A6: Store new assert winner as AssertWinner(S,G,I) and assert
724 winner metric as AssertWinnerMetric(S,G,I).
725 Set Assert Timer to Assert_Time.
726 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true)
727 set SPTbit(S,G) to true.
729 static void assert_action_a6(struct pim_ifchannel
*ch
,
730 struct pim_assert_metric winner_metric
)
732 assert_action_a2(ch
, winner_metric
);
735 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) set
738 if (ch
->upstream
->rpf
.source_nexthop
.interface
== ch
->interface
)
739 if (ch
->upstream
->join_state
== PIM_UPSTREAM_JOINED
)
740 ch
->upstream
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
742 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_LOSER
) {
743 if (PIM_DEBUG_PIM_EVENTS
)
745 "%s: channel%s not in PIM_IFASSERT_I_AM_LOSER state as expected",
746 __func__
, ch
->sg_str
);