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
),
65 char was_str
[INET_ADDRSTRLEN
];
66 char winner_str
[INET_ADDRSTRLEN
];
67 pim_inet4_dump("<was?>", ch
->ifassert_winner
, was_str
,
69 pim_inet4_dump("<winner?>", winner
, winner_str
,
72 "%s: (S,G)=%s assert winner changed from %s to %s on interface %s",
73 __func__
, ch
->sg_str
, was_str
, winner_str
,
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(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
,
93 if (PIM_DEBUG_PIM_TRACE
) {
94 char src_str
[INET_ADDRSTRLEN
];
95 pim_inet4_dump("<src?>", src
, src_str
, sizeof(src_str
));
96 zlog_debug("%s: from %s on %s", label
, src_str
, ifp
->name
);
100 static int preferred_assert(const struct pim_ifchannel
*ch
,
101 const struct pim_assert_metric
*recv_metric
)
103 return pim_assert_metric_better(recv_metric
,
104 &ch
->ifassert_winner_metric
);
107 static int acceptable_assert(const struct pim_assert_metric
*my_metric
,
108 const struct pim_assert_metric
*recv_metric
)
110 return pim_assert_metric_better(recv_metric
, my_metric
);
113 static int inferior_assert(const struct pim_assert_metric
*my_metric
,
114 const struct pim_assert_metric
*recv_metric
)
116 return pim_assert_metric_better(my_metric
, recv_metric
);
119 static int cancel_assert(const struct pim_assert_metric
*recv_metric
)
121 return (recv_metric
->metric_preference
122 == PIM_ASSERT_METRIC_PREFERENCE_MAX
)
123 && (recv_metric
->route_metric
== PIM_ASSERT_ROUTE_METRIC_MAX
);
126 static void if_could_assert_do_a1(const char *caller
, struct pim_ifchannel
*ch
)
128 if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
129 if (assert_action_a1(ch
)) {
131 "%s: %s: (S,G)=%s assert_action_a1 failure on interface %s",
132 __func__
, caller
, ch
->sg_str
,
133 ch
->interface
->name
);
134 /* log warning only */
139 static int dispatch_assert(struct interface
*ifp
, struct in_addr source_addr
,
140 struct in_addr group_addr
,
141 struct pim_assert_metric recv_metric
)
143 struct pim_ifchannel
*ch
;
146 memset(&sg
, 0, sizeof(sg
));
147 sg
.src
= source_addr
;
149 ch
= pim_ifchannel_add(ifp
, &sg
, 0, 0);
151 switch (ch
->ifassert_state
) {
152 case PIM_IFASSERT_NOINFO
:
153 if (recv_metric
.rpt_bit_flag
) {
155 if_could_assert_do_a1(__func__
, ch
);
158 if (inferior_assert(&ch
->ifassert_my_metric
,
160 if_could_assert_do_a1(__func__
, ch
);
161 } else if (acceptable_assert(&ch
->ifassert_my_metric
,
163 if (PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(
165 assert_action_a6(ch
, recv_metric
);
170 case PIM_IFASSERT_I_AM_WINNER
:
171 if (preferred_assert(ch
, &recv_metric
)) {
172 assert_action_a2(ch
, recv_metric
);
174 if (inferior_assert(&ch
->ifassert_my_metric
,
176 assert_action_a3(ch
);
180 case PIM_IFASSERT_I_AM_LOSER
:
181 if (!pim_addr_cmp(recv_metric
.ip_address
,
182 ch
->ifassert_winner
)) {
183 /* Assert from current winner */
185 if (cancel_assert(&recv_metric
)) {
186 assert_action_a5(ch
);
188 if (inferior_assert(&ch
->ifassert_my_metric
,
190 assert_action_a5(ch
);
191 } else if (acceptable_assert(
192 &ch
->ifassert_my_metric
,
194 if (!recv_metric
.rpt_bit_flag
) {
200 } else if (preferred_assert(ch
, &recv_metric
)) {
201 assert_action_a2(ch
, recv_metric
);
206 "%s: (S,G)=%s invalid assert state %d on interface %s",
207 __func__
, ch
->sg_str
, ch
->ifassert_state
, ifp
->name
);
215 int pim_assert_recv(struct interface
*ifp
, struct pim_neighbor
*neigh
,
216 struct in_addr src_addr
, uint8_t *buf
, int buf_size
)
219 struct prefix msg_source_addr
;
220 struct pim_assert_metric msg_metric
;
224 struct pim_interface
*pim_ifp
= NULL
;
226 on_trace(__func__
, ifp
, src_addr
);
229 curr_size
= buf_size
;
232 Parse assert group addr
234 memset(&sg
, 0, sizeof(sg
));
235 offset
= pim_parse_addr_group(&sg
, curr
, curr_size
);
237 char src_str
[INET_ADDRSTRLEN
];
238 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
239 zlog_warn("%s: pim_parse_addr_group() failure: from %s on %s",
240 __func__
, src_str
, ifp
->name
);
247 Parse assert source addr
249 offset
= pim_parse_addr_ucast(&msg_source_addr
, curr
, curr_size
);
251 char src_str
[INET_ADDRSTRLEN
];
252 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
253 zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
254 __func__
, src_str
, ifp
->name
);
261 char src_str
[INET_ADDRSTRLEN
];
262 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
264 "%s: preference/metric size is less than 8 bytes: size=%d from %s on interface %s",
265 __func__
, curr_size
, src_str
, ifp
->name
);
270 Parse assert metric preference
273 msg_metric
.metric_preference
= pim_read_uint32_host(curr
);
275 msg_metric
.rpt_bit_flag
= msg_metric
.metric_preference
276 & 0x80000000; /* save highest bit */
277 msg_metric
.metric_preference
&= ~0x80000000; /* clear highest bit */
282 Parse assert route metric
285 msg_metric
.route_metric
= pim_read_uint32_host(curr
);
287 if (PIM_DEBUG_PIM_TRACE
) {
288 char neigh_str
[INET_ADDRSTRLEN
];
289 char source_str
[INET_ADDRSTRLEN
];
290 char group_str
[INET_ADDRSTRLEN
];
291 pim_inet4_dump("<neigh?>", src_addr
, neigh_str
,
293 pim_inet4_dump("<src?>", msg_source_addr
.u
.prefix4
, source_str
,
295 pim_inet4_dump("<grp?>", sg
.grp
, group_str
, sizeof(group_str
));
297 "%s: from %s on %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
298 __func__
, neigh_str
, ifp
->name
, source_str
, group_str
,
299 msg_metric
.metric_preference
, msg_metric
.route_metric
,
300 PIM_FORCE_BOOLEAN(msg_metric
.rpt_bit_flag
));
303 msg_metric
.ip_address
= src_addr
;
307 ++pim_ifp
->pim_ifstat_assert_recv
;
309 return dispatch_assert(ifp
, msg_source_addr
.u
.prefix4
, sg
.grp
,
314 RFC 4601: 4.6.3. Assert Metrics
316 Assert metrics are defined as:
318 When comparing assert_metrics, the rpt_bit_flag, metric_preference,
319 and route_metric field are compared in order, where the first lower
320 value wins. If all fields are equal, the primary IP address of the
321 router that sourced the Assert message is used as a tie-breaker,
322 with the highest IP address winning.
324 int pim_assert_metric_better(const struct pim_assert_metric
*m1
,
325 const struct pim_assert_metric
*m2
)
327 if (m1
->rpt_bit_flag
< m2
->rpt_bit_flag
)
329 if (m1
->rpt_bit_flag
> m2
->rpt_bit_flag
)
332 if (m1
->metric_preference
< m2
->metric_preference
)
334 if (m1
->metric_preference
> m2
->metric_preference
)
337 if (m1
->route_metric
< m2
->route_metric
)
339 if (m1
->route_metric
> m2
->route_metric
)
342 return pim_addr_cmp(m1
->ip_address
, m2
->ip_address
) > 0;
345 int pim_assert_metric_match(const struct pim_assert_metric
*m1
,
346 const struct pim_assert_metric
*m2
)
348 if (m1
->rpt_bit_flag
!= m2
->rpt_bit_flag
)
350 if (m1
->metric_preference
!= m2
->metric_preference
)
352 if (m1
->route_metric
!= m2
->route_metric
)
355 return !pim_addr_cmp(m1
->ip_address
, m2
->ip_address
);
358 int pim_assert_build_msg(uint8_t *pim_msg
, int buf_size
, struct interface
*ifp
,
359 struct in_addr group_addr
, struct in_addr source_addr
,
360 uint32_t metric_preference
, uint32_t route_metric
,
361 uint32_t rpt_bit_flag
)
363 uint8_t *buf_pastend
= pim_msg
+ buf_size
;
364 uint8_t *pim_msg_curr
;
369 pim_msg
+ PIM_MSG_HEADER_LEN
; /* skip room for pim header */
372 remain
= buf_pastend
- pim_msg_curr
;
373 pim_msg_curr
= pim_msg_addr_encode_ipv4_group(pim_msg_curr
, group_addr
);
375 char group_str
[INET_ADDRSTRLEN
];
376 pim_inet4_dump("<grp?>", group_addr
, group_str
,
379 "%s: failure encoding group address %s: space left=%d",
380 __func__
, group_str
, remain
);
385 remain
= buf_pastend
- pim_msg_curr
;
387 pim_msg_addr_encode_ipv4_ucast(pim_msg_curr
, source_addr
);
389 char source_str
[INET_ADDRSTRLEN
];
390 pim_inet4_dump("<src?>", source_addr
, source_str
,
393 "%s: failure encoding source address %s: space left=%d",
394 __func__
, source_str
, remain
);
398 /* Metric preference */
399 pim_write_uint32(pim_msg_curr
,
400 rpt_bit_flag
? metric_preference
| 0x80000000
401 : metric_preference
);
405 pim_write_uint32(pim_msg_curr
, route_metric
);
411 pim_msg_size
= pim_msg_curr
- pim_msg
;
412 pim_msg_build_header(pim_msg
, pim_msg_size
, PIM_MSG_TYPE_ASSERT
, false);
417 static int pim_assert_do(struct pim_ifchannel
*ch
,
418 struct pim_assert_metric metric
)
420 struct interface
*ifp
;
421 struct pim_interface
*pim_ifp
;
422 uint8_t pim_msg
[1000];
427 if (PIM_DEBUG_PIM_TRACE
)
428 zlog_debug("%s: channel%s has no associated interface!",
429 __func__
, ch
->sg_str
);
434 if (PIM_DEBUG_PIM_TRACE
)
436 "%s: channel %s pim not enabled on interface: %s",
437 __func__
, ch
->sg_str
, ifp
->name
);
442 pim_assert_build_msg(pim_msg
, sizeof(pim_msg
), ifp
, ch
->sg
.grp
,
443 ch
->sg
.src
, metric
.metric_preference
,
444 metric
.route_metric
, metric
.rpt_bit_flag
);
445 if (pim_msg_size
< 1) {
447 "%s: failure building PIM assert message: msg_size=%d",
448 __func__
, pim_msg_size
);
453 RFC 4601: 4.3.1. Sending Hello Messages
455 Thus, if a router needs to send a Join/Prune or Assert message on
456 an interface on which it has not yet sent a Hello message with the
457 currently configured IP address, then it MUST immediately send the
458 relevant Hello message without waiting for the Hello Timer to
459 expire, followed by the Join/Prune or Assert message.
461 pim_hello_require(ifp
);
463 if (PIM_DEBUG_PIM_TRACE
) {
464 zlog_debug("%s: to %s: (S,G)=%s pref=%u metric=%u rpt_bit=%u",
465 __func__
, ifp
->name
, ch
->sg_str
,
466 metric
.metric_preference
, metric
.route_metric
,
467 PIM_FORCE_BOOLEAN(metric
.rpt_bit_flag
));
469 ++pim_ifp
->pim_ifstat_assert_send
;
471 if (pim_msg_send(pim_ifp
->pim_sock_fd
, pim_ifp
->primary_address
,
472 qpim_all_pim_routers_addr
, pim_msg
, pim_msg_size
,
474 zlog_warn("%s: could not send PIM message on interface %s",
475 __func__
, ifp
->name
);
482 int pim_assert_send(struct pim_ifchannel
*ch
)
484 return pim_assert_do(ch
, ch
->ifassert_my_metric
);
488 RFC 4601: 4.6.4. AssertCancel Messages
490 An AssertCancel(S,G) is an infinite metric assert with the RPT bit
491 set that names S as the source.
493 static int pim_assert_cancel(struct pim_ifchannel
*ch
)
495 struct pim_assert_metric metric
;
497 metric
.rpt_bit_flag
= 0;
498 metric
.metric_preference
= PIM_ASSERT_METRIC_PREFERENCE_MAX
;
499 metric
.route_metric
= PIM_ASSERT_ROUTE_METRIC_MAX
;
500 metric
.ip_address
= ch
->sg
.src
;
502 return pim_assert_do(ch
, metric
);
505 static int on_assert_timer(struct thread
*t
)
507 struct pim_ifchannel
*ch
;
508 struct interface
*ifp
;
514 if (PIM_DEBUG_PIM_TRACE
) {
515 zlog_debug("%s: (S,G)=%s timer expired on interface %s",
516 __func__
, ch
->sg_str
, ifp
->name
);
519 ch
->t_ifassert_timer
= NULL
;
521 switch (ch
->ifassert_state
) {
522 case PIM_IFASSERT_I_AM_WINNER
:
523 assert_action_a3(ch
);
525 case PIM_IFASSERT_I_AM_LOSER
:
526 assert_action_a5(ch
);
529 if (PIM_DEBUG_PIM_EVENTS
)
531 "%s: (S,G)=%s invalid assert state %d on interface %s",
532 __func__
, ch
->sg_str
, ch
->ifassert_state
,
540 static void assert_timer_off(struct pim_ifchannel
*ch
)
542 if (PIM_DEBUG_PIM_TRACE
) {
543 if (ch
->t_ifassert_timer
) {
545 "%s: (S,G)=%s cancelling timer on interface %s",
546 __func__
, ch
->sg_str
, ch
->interface
->name
);
549 THREAD_OFF(ch
->t_ifassert_timer
);
552 static void pim_assert_timer_set(struct pim_ifchannel
*ch
, int interval
)
554 assert_timer_off(ch
);
556 if (PIM_DEBUG_PIM_TRACE
) {
557 zlog_debug("%s: (S,G)=%s starting %u sec timer on interface %s",
558 __func__
, ch
->sg_str
, interval
, ch
->interface
->name
);
561 thread_add_timer(router
->master
, on_assert_timer
, ch
, interval
,
562 &ch
->t_ifassert_timer
);
565 static void pim_assert_timer_reset(struct pim_ifchannel
*ch
)
567 pim_assert_timer_set(ch
,
568 PIM_ASSERT_TIME
- PIM_ASSERT_OVERRIDE_INTERVAL
);
572 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
574 (S,G) Assert State machine Actions
576 A1: Send Assert(S,G).
577 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
578 Store self as AssertWinner(S,G,I).
579 Store spt_assert_metric(S,I) as AssertWinnerMetric(S,G,I).
581 int assert_action_a1(struct pim_ifchannel
*ch
)
583 struct interface
*ifp
= ch
->interface
;
584 struct pim_interface
*pim_ifp
;
588 zlog_warn("%s: (S,G)=%s multicast not enabled on interface %s",
589 __func__
, ch
->sg_str
, ifp
->name
);
590 return -1; /* must return since pim_ifp is used below */
593 /* Switch to I_AM_WINNER before performing action_a3 below */
594 pim_ifassert_winner_set(
595 ch
, PIM_IFASSERT_I_AM_WINNER
, pim_ifp
->primary_address
,
596 pim_macro_spt_assert_metric(&ch
->upstream
->rpf
,
597 pim_ifp
->primary_address
));
599 if (assert_action_a3(ch
)) {
601 "%s: (S,G)=%s assert_action_a3 failure on interface %s",
602 __func__
, ch
->sg_str
, ifp
->name
);
606 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_WINNER
) {
607 if (PIM_DEBUG_PIM_EVENTS
)
609 "%s: channel%s not in expected PIM_IFASSERT_I_AM_WINNER state",
610 __func__
, ch
->sg_str
);
617 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
619 (S,G) Assert State machine Actions
621 A2: Store new assert winner as AssertWinner(S,G,I) and assert
622 winner metric as AssertWinnerMetric(S,G,I).
623 Set Assert Timer to Assert_Time.
625 static void assert_action_a2(struct pim_ifchannel
*ch
,
626 struct pim_assert_metric winner_metric
)
628 pim_ifassert_winner_set(ch
, PIM_IFASSERT_I_AM_LOSER
,
629 winner_metric
.ip_address
, winner_metric
);
631 pim_assert_timer_set(ch
, PIM_ASSERT_TIME
);
633 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_LOSER
) {
634 if (PIM_DEBUG_PIM_EVENTS
)
636 "%s: channel%s not in expected PIM_IFASSERT_I_AM_LOSER state",
637 __func__
, ch
->sg_str
);
642 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
644 (S,G) Assert State machine Actions
646 A3: Send Assert(S,G).
647 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
649 static int assert_action_a3(struct pim_ifchannel
*ch
)
651 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_WINNER
) {
652 if (PIM_DEBUG_PIM_EVENTS
)
654 "%s: channel%s expected to be in PIM_IFASSERT_I_AM_WINNER state",
655 __func__
, ch
->sg_str
);
659 pim_assert_timer_reset(ch
);
661 if (pim_assert_send(ch
)) {
662 zlog_warn("%s: (S,G)=%s failure sending assert on interface %s",
663 __func__
, ch
->sg_str
, ch
->interface
->name
);
671 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
673 (S,G) Assert State machine Actions
675 A4: Send AssertCancel(S,G).
676 Delete assert info (AssertWinner(S,G,I) and
677 AssertWinnerMetric(S,G,I) will then return their default
680 void assert_action_a4(struct pim_ifchannel
*ch
)
682 if (pim_assert_cancel(ch
)) {
683 zlog_warn("%s: failure sending AssertCancel%s on interface %s",
684 __func__
, ch
->sg_str
, ch
->interface
->name
);
685 /* log warning only */
688 assert_action_a5(ch
);
690 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
691 if (PIM_DEBUG_PIM_EVENTS
)
693 "%s: channel%s not in PIM_IFASSERT_NOINFO state as expected",
694 __func__
, ch
->sg_str
);
699 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
701 (S,G) Assert State machine Actions
703 A5: Delete assert info (AssertWinner(S,G,I) and
704 AssertWinnerMetric(S,G,I) will then return their default values).
706 void assert_action_a5(struct pim_ifchannel
*ch
)
708 reset_ifassert_state(ch
);
709 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
710 if (PIM_DEBUG_PIM_EVENTS
)
712 "%s: channel%s not in PIM_IFSSERT_NOINFO state as expected",
713 __func__
, ch
->sg_str
);
718 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
720 (S,G) Assert State machine Actions
722 A6: Store new assert winner as AssertWinner(S,G,I) and assert
723 winner metric as AssertWinnerMetric(S,G,I).
724 Set Assert Timer to Assert_Time.
725 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true)
726 set SPTbit(S,G) to true.
728 static void assert_action_a6(struct pim_ifchannel
*ch
,
729 struct pim_assert_metric winner_metric
)
731 assert_action_a2(ch
, winner_metric
);
734 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) set
737 if (ch
->upstream
->rpf
.source_nexthop
.interface
== ch
->interface
)
738 if (ch
->upstream
->join_state
== PIM_UPSTREAM_JOINED
)
739 ch
->upstream
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
741 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_LOSER
) {
742 if (PIM_DEBUG_PIM_EVENTS
)
744 "%s: channel%s not in PIM_IFASSERT_I_AM_LOSER state as expected",
745 __func__
, ch
->sg_str
);