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,
20 $QuaggaId: $Format:%an, %ai, %h$ $
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) {
60 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
61 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
62 zlog_debug("%s: (S,G)=(%s,%s) assert state changed from %s to %s on interface %s",
65 pim_ifchannel_ifassert_name(ch->ifassert_state),
66 pim_ifchannel_ifassert_name(new_state),
75 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
76 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
77 pim_inet4_dump("<was?>", ch->ifassert_winner, was_str, sizeof(was_str));
78 pim_inet4_dump("<winner?>", winner, winner_str, sizeof(winner_str));
79 zlog_debug("%s: (S,G)=(%s,%s) assert winner changed from %s to %s on interface %s",
82 was_str, winner_str, ch->interface->name);
84 } /* PIM_DEBUG_PIM_EVENTS */
86 ch->ifassert_state = new_state;
87 ch->ifassert_winner = winner;
88 ch->ifassert_winner_metric = winner_metric;
89 ch->ifassert_creation = pim_time_monotonic_sec();
91 if (winner_changed || metric_changed) {
92 pim_upstream_update_join_desired(ch->upstream);
93 pim_ifchannel_update_could_assert(ch);
94 pim_ifchannel_update_assert_tracking_desired(ch);
98 static void on_trace(const char *label,
99 struct interface *ifp, struct in_addr src)
101 if (PIM_DEBUG_PIM_TRACE) {
103 pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
104 zlog_debug("%s: from %s on %s",
105 label, src_str, ifp->name);
109 static int preferred_assert(const struct pim_ifchannel *ch,
110 const struct pim_assert_metric *recv_metric)
112 return pim_assert_metric_better(recv_metric,
113 &ch->ifassert_winner_metric);
116 static int acceptable_assert(const struct pim_assert_metric *my_metric,
117 const struct pim_assert_metric *recv_metric)
119 return pim_assert_metric_better(recv_metric,
123 static int inferior_assert(const struct pim_assert_metric *my_metric,
124 const struct pim_assert_metric *recv_metric)
126 return pim_assert_metric_better(my_metric,
130 static int cancel_assert(const struct pim_assert_metric *recv_metric)
132 return (recv_metric->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX)
134 (recv_metric->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX);
137 static void if_could_assert_do_a1(const char *caller,
138 struct pim_ifchannel *ch)
140 if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
141 if (assert_action_a1(ch)) {
144 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
145 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
146 zlog_warn("%s: %s: (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
147 __PRETTY_FUNCTION__, caller,
148 src_str, grp_str, ch->interface->name);
149 /* log warning only */
154 static int dispatch_assert(struct interface *ifp,
155 struct in_addr source_addr,
156 struct in_addr group_addr,
157 struct pim_assert_metric recv_metric)
159 struct pim_ifchannel *ch;
161 ch = pim_ifchannel_add(ifp, source_addr, group_addr);
163 char source_str[100];
165 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
166 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
167 zlog_warn("%s: (S,G)=(%s,%s) failure creating channel on interface %s",
169 source_str, group_str, ifp->name);
173 switch (ch->ifassert_state) {
174 case PIM_IFASSERT_NOINFO:
175 if (recv_metric.rpt_bit_flag) {
177 if_could_assert_do_a1(__PRETTY_FUNCTION__, ch);
181 if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
182 if_could_assert_do_a1(__PRETTY_FUNCTION__, ch);
184 else if (acceptable_assert(&ch->ifassert_my_metric, &recv_metric)) {
185 if (PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags)) {
186 assert_action_a6(ch, recv_metric);
191 case PIM_IFASSERT_I_AM_WINNER:
192 if (preferred_assert(ch, &recv_metric)) {
193 assert_action_a2(ch, recv_metric);
196 if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
197 zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
198 assert_action_a3(ch);
202 case PIM_IFASSERT_I_AM_LOSER:
203 if (recv_metric.ip_address.s_addr == ch->ifassert_winner.s_addr) {
204 /* Assert from current winner */
206 if (cancel_assert(&recv_metric)) {
207 assert_action_a5(ch);
210 if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
211 assert_action_a5(ch);
213 else if (acceptable_assert(&ch->ifassert_my_metric, &recv_metric)) {
214 if (!recv_metric.rpt_bit_flag) {
215 assert_action_a2(ch, recv_metric);
220 else if (preferred_assert(ch, &recv_metric)) {
221 assert_action_a2(ch, recv_metric);
226 char source_str[100];
228 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
229 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
230 zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s",
232 source_str, group_str, ch->ifassert_state, ifp->name);
240 int pim_assert_recv(struct interface *ifp,
241 struct pim_neighbor *neigh,
242 struct in_addr src_addr,
243 uint8_t *buf, int buf_size)
245 struct prefix msg_group_addr;
246 struct prefix msg_source_addr;
247 struct pim_assert_metric msg_metric;
252 on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
255 curr_size = buf_size;
258 Parse assert group addr
260 offset = pim_parse_addr_group(ifp->name, src_addr,
265 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
266 zlog_warn("%s: pim_parse_addr_group() failure: from %s on %s",
275 Parse assert source addr
277 offset = pim_parse_addr_ucast(ifp->name, src_addr,
282 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
283 zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
291 if (curr_size != 8) {
293 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
294 zlog_warn("%s: preference/metric size is not 8: size=%d from %s on interface %s",
302 Parse assert metric preference
305 msg_metric.metric_preference = pim_read_uint32_host(curr);
307 msg_metric.rpt_bit_flag = msg_metric.metric_preference & 0x80000000; /* save highest bit */
308 msg_metric.metric_preference &= ~0x80000000; /* clear highest bit */
313 Parse assert route metric
316 msg_metric.route_metric = pim_read_uint32_host(curr);
318 if (PIM_DEBUG_PIM_TRACE) {
320 char source_str[100];
322 pim_inet4_dump("<neigh?>", src_addr, neigh_str, sizeof(neigh_str));
323 pim_inet4_dump("<src?>", msg_source_addr.u.prefix4, source_str, sizeof(source_str));
324 pim_inet4_dump("<grp?>", msg_group_addr.u.prefix4, group_str, sizeof(group_str));
325 zlog_debug("%s: from %s on %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
326 __PRETTY_FUNCTION__, neigh_str, ifp->name,
327 source_str, group_str,
328 msg_metric.metric_preference,
329 msg_metric.route_metric,
330 PIM_FORCE_BOOLEAN(msg_metric.rpt_bit_flag));
333 msg_metric.ip_address = src_addr;
335 return dispatch_assert(ifp,
336 msg_source_addr.u.prefix4,
337 msg_group_addr.u.prefix4,
342 RFC 4601: 4.6.3. Assert Metrics
344 Assert metrics are defined as:
346 When comparing assert_metrics, the rpt_bit_flag, metric_preference,
347 and route_metric field are compared in order, where the first lower
348 value wins. If all fields are equal, the primary IP address of the
349 router that sourced the Assert message is used as a tie-breaker,
350 with the highest IP address winning.
352 int pim_assert_metric_better(const struct pim_assert_metric *m1,
353 const struct pim_assert_metric *m2)
355 if (m1->rpt_bit_flag < m2->rpt_bit_flag)
357 if (m1->rpt_bit_flag > m2->rpt_bit_flag)
360 if (m1->metric_preference < m2->metric_preference)
362 if (m1->metric_preference > m2->metric_preference)
365 if (m1->route_metric < m2->route_metric)
367 if (m1->route_metric > m2->route_metric)
370 return ntohl(m1->ip_address.s_addr) > ntohl(m2->ip_address.s_addr);
373 int pim_assert_metric_match(const struct pim_assert_metric *m1,
374 const struct pim_assert_metric *m2)
376 if (m1->rpt_bit_flag != m2->rpt_bit_flag)
378 if (m1->metric_preference != m2->metric_preference)
380 if (m1->route_metric != m2->route_metric)
383 return m1->ip_address.s_addr == m2->ip_address.s_addr;
386 int pim_assert_build_msg(uint8_t *pim_msg, int buf_size,
387 struct interface *ifp,
388 struct in_addr group_addr,
389 struct in_addr source_addr,
390 uint32_t metric_preference,
391 uint32_t route_metric,
392 uint32_t rpt_bit_flag)
394 uint8_t *buf_pastend = pim_msg + buf_size;
395 uint8_t *pim_msg_curr;
399 pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* skip room for pim header */
402 remain = buf_pastend - pim_msg_curr;
403 pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
408 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
409 zlog_warn("%s: failure encoding group address %s: space left=%d",
410 __PRETTY_FUNCTION__, group_str, remain);
415 remain = buf_pastend - pim_msg_curr;
416 pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
420 char source_str[100];
421 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
422 zlog_warn("%s: failure encoding source address %s: space left=%d",
423 __PRETTY_FUNCTION__, source_str, remain);
427 /* Metric preference */
428 pim_write_uint32(pim_msg_curr, rpt_bit_flag ?
429 metric_preference | 0x80000000 :
434 pim_write_uint32(pim_msg_curr, route_metric);
440 pim_msg_size = pim_msg_curr - pim_msg;
441 pim_msg_build_header(pim_msg, pim_msg_size,
442 PIM_MSG_TYPE_ASSERT);
447 static int pim_assert_do(struct pim_ifchannel *ch,
448 struct pim_assert_metric metric)
450 struct interface *ifp;
451 struct pim_interface *pim_ifp;
452 uint8_t pim_msg[1000];
460 zlog_warn("%s: pim not enabled on interface: %s",
461 __PRETTY_FUNCTION__, ifp->name);
465 pim_msg_size = pim_assert_build_msg(pim_msg, sizeof(pim_msg), ifp,
466 ch->group_addr, ch->source_addr,
467 metric.metric_preference,
469 metric.rpt_bit_flag);
470 if (pim_msg_size < 1) {
471 zlog_warn("%s: failure building PIM assert message: msg_size=%d",
472 __PRETTY_FUNCTION__, pim_msg_size);
477 RFC 4601: 4.3.1. Sending Hello Messages
479 Thus, if a router needs to send a Join/Prune or Assert message on
480 an interface on which it has not yet sent a Hello message with the
481 currently configured IP address, then it MUST immediately send the
482 relevant Hello message without waiting for the Hello Timer to
483 expire, followed by the Join/Prune or Assert message.
485 pim_hello_require(ifp);
487 if (PIM_DEBUG_PIM_TRACE) {
488 char source_str[100];
490 pim_inet4_dump("<src?>", ch->source_addr, source_str, sizeof(source_str));
491 pim_inet4_dump("<grp?>", ch->group_addr, group_str, sizeof(group_str));
492 zlog_debug("%s: to %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
494 ifp->name, source_str, group_str,
495 metric.metric_preference,
497 PIM_FORCE_BOOLEAN(metric.rpt_bit_flag));
500 if (pim_msg_send(pim_ifp->pim_sock_fd,
501 qpim_all_pim_routers_addr,
505 zlog_warn("%s: could not send PIM message on interface %s",
506 __PRETTY_FUNCTION__, ifp->name);
513 int pim_assert_send(struct pim_ifchannel *ch)
515 return pim_assert_do(ch, ch->ifassert_my_metric);
519 RFC 4601: 4.6.4. AssertCancel Messages
521 An AssertCancel(S,G) is an infinite metric assert with the RPT bit
522 set that names S as the source.
524 static int pim_assert_cancel(struct pim_ifchannel *ch)
526 struct pim_assert_metric metric;
528 metric.rpt_bit_flag = 0;
529 metric.metric_preference = PIM_ASSERT_METRIC_PREFERENCE_MAX;
530 metric.route_metric = PIM_ASSERT_ROUTE_METRIC_MAX;
531 metric.ip_address = ch->source_addr;
533 return pim_assert_do(ch, metric);
536 static int on_assert_timer(struct thread *t)
538 struct pim_ifchannel *ch;
539 struct interface *ifp;
548 if (PIM_DEBUG_PIM_TRACE) {
551 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
552 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
553 zlog_debug("%s: (S,G)=(%s,%s) timer expired on interface %s",
555 src_str, grp_str, ifp->name);
558 ch->t_ifassert_timer = 0;
560 switch (ch->ifassert_state) {
561 case PIM_IFASSERT_I_AM_WINNER:
562 zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
563 assert_action_a3(ch);
565 case PIM_IFASSERT_I_AM_LOSER:
566 assert_action_a5(ch);
570 char source_str[100];
572 pim_inet4_dump("<src?>", ch->source_addr, source_str, sizeof(source_str));
573 pim_inet4_dump("<grp?>", ch->group_addr, group_str, sizeof(group_str));
574 zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s",
576 source_str, group_str, ch->ifassert_state, ifp->name);
583 static void assert_timer_off(struct pim_ifchannel *ch)
585 struct interface *ifp;
591 if (PIM_DEBUG_PIM_TRACE) {
592 if (ch->t_ifassert_timer) {
595 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
596 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
597 zlog_debug("%s: (S,G)=(%s,%s) cancelling timer on interface %s",
599 src_str, grp_str, ifp->name);
602 THREAD_OFF(ch->t_ifassert_timer);
603 zassert(!ch->t_ifassert_timer);
606 static void pim_assert_timer_set(struct pim_ifchannel *ch,
609 struct interface *ifp;
615 assert_timer_off(ch);
617 if (PIM_DEBUG_PIM_TRACE) {
620 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
621 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
622 zlog_debug("%s: (S,G)=(%s,%s) starting %u sec timer on interface %s",
624 src_str, grp_str, interval, ifp->name);
627 THREAD_TIMER_ON(master, ch->t_ifassert_timer,
632 static void pim_assert_timer_reset(struct pim_ifchannel *ch)
634 pim_assert_timer_set(ch, PIM_ASSERT_TIME - PIM_ASSERT_OVERRIDE_INTERVAL);
638 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
640 (S,G) Assert State machine Actions
642 A1: Send Assert(S,G).
643 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
644 Store self as AssertWinner(S,G,I).
645 Store spt_assert_metric(S,I) as AssertWinnerMetric(S,G,I).
647 int assert_action_a1(struct pim_ifchannel *ch)
649 struct interface *ifp = ch->interface;
650 struct pim_interface *pim_ifp;
658 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
659 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
660 zlog_warn("%s: (S,G)=(%s,%s) multicast not enabled on interface %s",
662 src_str, grp_str, ifp->name);
663 return -1; /* must return since pim_ifp is used below */
666 /* Switch to I_AM_WINNER before performing action_a3 below */
667 pim_ifassert_winner_set(ch, PIM_IFASSERT_I_AM_WINNER,
668 pim_ifp->primary_address,
669 pim_macro_spt_assert_metric(&ch->upstream->rpf,
670 pim_ifp->primary_address));
672 zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
673 if (assert_action_a3(ch)) {
676 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
677 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
678 zlog_warn("%s: (S,G)=(%s,%s) assert_action_a3 failure on interface %s",
680 src_str, grp_str, ifp->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 A2: 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.
698 static void assert_action_a2(struct pim_ifchannel *ch,
699 struct pim_assert_metric winner_metric)
701 pim_ifassert_winner_set(ch, PIM_IFASSERT_I_AM_LOSER,
702 winner_metric.ip_address,
705 pim_assert_timer_set(ch, PIM_ASSERT_TIME);
707 zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER);
711 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
713 (S,G) Assert State machine Actions
715 A3: Send Assert(S,G).
716 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
718 static int assert_action_a3(struct pim_ifchannel *ch)
720 zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
722 pim_assert_timer_reset(ch);
724 if (pim_assert_send(ch)) {
727 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
728 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
730 zlog_warn("%s: (S,G)=(%s,%s) failure sending assert on interface %s",
732 src_str, grp_str, ch->interface->name);
736 zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
742 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
744 (S,G) Assert State machine Actions
746 A4: Send AssertCancel(S,G).
747 Delete assert info (AssertWinner(S,G,I) and
748 AssertWinnerMetric(S,G,I) will then return their default
751 void assert_action_a4(struct pim_ifchannel *ch)
753 if (pim_assert_cancel(ch)) {
756 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
757 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
758 zlog_warn("%s: failure sending AssertCancel(%s,%s) on interface %s",
760 src_str, grp_str, ch->interface->name);
761 /* log warning only */
764 assert_action_a5(ch);
766 zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO);
770 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
772 (S,G) Assert State machine Actions
774 A5: Delete assert info (AssertWinner(S,G,I) and
775 AssertWinnerMetric(S,G,I) will then return their default values).
777 void assert_action_a5(struct pim_ifchannel *ch)
779 reset_ifassert_state(ch);
780 zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO);
784 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
786 (S,G) Assert State machine Actions
788 A6: Store new assert winner as AssertWinner(S,G,I) and assert
789 winner metric as AssertWinnerMetric(S,G,I).
790 Set Assert Timer to Assert_Time.
791 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true)
792 set SPTbit(S,G) to TRUE.
794 static void assert_action_a6(struct pim_ifchannel *ch,
795 struct pim_assert_metric winner_metric)
797 assert_action_a2(ch, winner_metric);
800 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) set
803 Notice: For PIM SSM, SPTbit(S,G) is already always true.
806 zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER);