[pim] Initial pim 0.155
diff --git a/pimd/pim_assert.c b/pimd/pim_assert.c
new file mode 100644
index 0000000..0ce0e5d
--- /dev/null
+++ b/pimd/pim_assert.c
@@ -0,0 +1,803 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+  
+  You should have received a copy of the GNU General Public License
+  along with this program; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "prefix.h"
+
+#include "pimd.h"
+#include "pim_str.h"
+#include "pim_tlv.h"
+#include "pim_msg.h"
+#include "pim_pim.h"
+#include "pim_time.h"
+#include "pim_iface.h"
+#include "pim_hello.h"
+#include "pim_macro.h"
+#include "pim_assert.h"
+#include "pim_ifchannel.h"
+
+static int assert_action_a3(struct pim_ifchannel *ch);
+static void assert_action_a2(struct pim_ifchannel *ch,
+			     struct pim_assert_metric winner_metric);
+static void assert_action_a6(struct pim_ifchannel *ch,
+			     struct pim_assert_metric winner_metric);
+
+void pim_ifassert_winner_set(struct pim_ifchannel     *ch,
+			     enum pim_ifassert_state   new_state,
+			     struct in_addr            winner,
+			     struct pim_assert_metric  winner_metric)
+{
+  int winner_changed = (ch->ifassert_winner.s_addr != winner.s_addr);
+  int metric_changed = !pim_assert_metric_match(&ch->ifassert_winner_metric,
+						&winner_metric);
+
+  if (ch->ifassert_state != new_state) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_info("%s: (S,G)=(%s,%s) assert state changed from %s to %s on interface %s",
+	      __PRETTY_FUNCTION__,
+	      src_str, grp_str,
+	      pim_ifchannel_ifassert_name(ch->ifassert_state),
+	      pim_ifchannel_ifassert_name(new_state),
+	      ch->interface->name);
+  }
+
+  {
+    char src_str[100];
+    char grp_str[100];
+    char winner_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    pim_inet4_dump("<winner?>", winner, winner_str, sizeof(winner_str));
+    zlog_info("%s: (S,G)=(%s,%s) assert winner now is %s on interface %s",
+	      __PRETTY_FUNCTION__,
+	      src_str, grp_str,
+	      winner_str, ch->interface->name);
+  }
+
+  ch->ifassert_state         = new_state;
+  ch->ifassert_winner        = winner;
+  ch->ifassert_winner_metric = winner_metric;
+  ch->ifassert_creation      = pim_time_monotonic_sec();
+
+  if (winner_changed || metric_changed) {
+    pim_upstream_update_join_desired(ch->upstream);
+    pim_ifchannel_update_could_assert(ch);
+    pim_ifchannel_update_assert_tracking_desired(ch);
+  }
+}
+
+static void on_trace(const char *label,
+		     struct interface *ifp, struct in_addr src)
+{
+  if (PIM_DEBUG_PIM_TRACE) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
+    zlog_debug("%s: from %s on %s",
+	       label, src_str, ifp->name);
+  }
+}
+
+static int preferred_assert(const struct pim_ifchannel *ch,
+			    const struct pim_assert_metric *recv_metric)
+{
+  return pim_assert_metric_better(recv_metric,
+				  &ch->ifassert_winner_metric);
+}
+
+static int acceptable_assert(const struct pim_assert_metric *my_metric,
+			     const struct pim_assert_metric *recv_metric)
+{
+  return pim_assert_metric_better(recv_metric,
+				  my_metric);
+}
+
+static int inferior_assert(const struct pim_assert_metric *my_metric,
+			   const struct pim_assert_metric *recv_metric)
+{
+  return pim_assert_metric_better(my_metric,
+				  recv_metric);
+}
+
+static int cancel_assert(const struct pim_assert_metric *recv_metric)
+{
+  return (recv_metric->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX)
+    &&
+    (recv_metric->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX);
+}
+
+static void if_could_assert_do_a1(const char *caller,
+				  struct pim_ifchannel *ch)
+{
+  if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
+    if (assert_action_a1(ch)) {
+      char src_str[100];
+      char grp_str[100];
+      pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+      pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+      zlog_warn("%s: %s: (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
+		__PRETTY_FUNCTION__, caller,
+		src_str, grp_str, ch->interface->name);
+      /* log warning only */
+    }
+  }
+}
+
+static int dispatch_assert(struct interface *ifp,
+			   struct in_addr source_addr,
+			   struct in_addr group_addr,
+			   struct pim_assert_metric recv_metric)
+{
+  struct pim_ifchannel *ch;
+
+  ch = pim_ifchannel_add(ifp, source_addr, group_addr);
+  if (!ch) {
+    char source_str[100];
+    char group_str[100];
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+    zlog_warn("%s: (S,G)=(%s,%s) failure creating channel on interface %s",
+	      __PRETTY_FUNCTION__,
+	      source_str, group_str, ifp->name);
+    return -1;
+  }
+
+  switch (ch->ifassert_state) {
+  case PIM_IFASSERT_NOINFO:
+    if (recv_metric.rpt_bit_flag) {
+      /* RPT bit set */
+      if_could_assert_do_a1(__PRETTY_FUNCTION__, ch);
+    }
+    else {
+      /* RPT bit clear */
+      if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
+	if_could_assert_do_a1(__PRETTY_FUNCTION__, ch);
+      }
+      else if (acceptable_assert(&ch->ifassert_my_metric, &recv_metric)) {
+	if (PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags)) {
+	  assert_action_a6(ch, recv_metric);
+	}
+      }
+    }
+    break;
+  case PIM_IFASSERT_I_AM_WINNER:
+    if (preferred_assert(ch, &recv_metric)) {
+      assert_action_a2(ch, recv_metric);
+    }
+    else {
+      if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
+	zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
+	assert_action_a3(ch);
+      }
+    }
+    break;
+  case PIM_IFASSERT_I_AM_LOSER:
+    if (recv_metric.ip_address.s_addr == ch->ifassert_winner.s_addr) {
+      /* Assert from current winner */
+
+      if (cancel_assert(&recv_metric)) {
+	assert_action_a5(ch);
+      }
+      else {
+	if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
+	  assert_action_a5(ch);
+	}
+	else if (acceptable_assert(&ch->ifassert_my_metric, &recv_metric)) {
+	  if (!recv_metric.rpt_bit_flag) {
+	    assert_action_a2(ch, recv_metric);
+	  }
+	}
+      }
+    }
+    else if (preferred_assert(ch, &recv_metric)) {
+      assert_action_a2(ch, recv_metric);
+    }
+    break;
+  default:
+    {
+      char source_str[100];
+      char group_str[100];
+      pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+      pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+      zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s",
+		__PRETTY_FUNCTION__,
+		source_str, group_str, ch->ifassert_state, ifp->name);
+    }
+    return -2;
+  }
+
+  return 0;
+}
+
+int pim_assert_recv(struct interface *ifp,
+		    struct pim_neighbor *neigh,
+		    struct in_addr src_addr,
+		    char *buf, int buf_size)
+{
+  struct prefix            msg_group_addr;
+  struct prefix            msg_source_addr;
+  struct pim_assert_metric msg_metric;
+  int offset;
+  char *curr;
+  int curr_size;
+
+  on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
+
+  curr      = buf;
+  curr_size = buf_size;
+
+  /*
+    Parse assert group addr
+   */
+  offset = pim_parse_addr_group(ifp->name, src_addr,
+				&msg_group_addr,
+				curr, curr_size);
+  if (offset < 1) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_warn("%s: pim_parse_addr_group() failure: from %s on %s",
+	      __PRETTY_FUNCTION__,
+	      src_str, ifp->name);
+    return -1;
+  }
+  curr      += offset;
+  curr_size -= offset;
+
+  /*
+    Parse assert source addr
+  */
+  offset = pim_parse_addr_ucast(ifp->name, src_addr,
+				&msg_source_addr,
+				curr, curr_size);
+  if (offset < 1) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
+	      __PRETTY_FUNCTION__,
+	      src_str, ifp->name);
+    return -2;
+  }
+  curr      += offset;
+  curr_size -= offset;
+
+  if (curr_size != 8) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_warn("%s: preference/metric size is not 8: size=%d from %s on interface %s",
+	      __PRETTY_FUNCTION__,
+	      curr_size,
+	      src_str, ifp->name);
+    return -3;
+  }
+
+  /*
+    Parse assert metric preference
+  */
+
+  msg_metric.metric_preference = ntohl(*(const uint32_t *) curr);
+
+  msg_metric.rpt_bit_flag = msg_metric.metric_preference & 0x80000000; /* save highest bit */
+  msg_metric.metric_preference &= ~0x80000000; /* clear highest bit */
+
+  curr += 4;
+
+  /*
+    Parse assert route metric
+  */
+
+  msg_metric.route_metric = ntohl(*(const uint32_t *) curr);
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    char neigh_str[100];
+    char source_str[100];
+    char group_str[100];
+    pim_inet4_dump("<neigh?>", src_addr, neigh_str, sizeof(neigh_str));
+    pim_inet4_dump("<src?>", msg_source_addr.u.prefix4, source_str, sizeof(source_str));
+    pim_inet4_dump("<grp?>", msg_group_addr.u.prefix4, group_str, sizeof(group_str));
+    zlog_debug("%s: from %s on %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
+	       __PRETTY_FUNCTION__, neigh_str, ifp->name,
+	       source_str, group_str,
+	       msg_metric.metric_preference,
+	       msg_metric.route_metric,
+	       PIM_FORCE_BOOLEAN(msg_metric.rpt_bit_flag));
+  }
+
+  msg_metric.ip_address = src_addr;
+
+  return dispatch_assert(ifp,
+			 msg_source_addr.u.prefix4,
+			 msg_group_addr.u.prefix4,
+			 msg_metric);
+}
+
+/*
+  RFC 4601: 4.6.3.  Assert Metrics
+
+   Assert metrics are defined as:
+
+   When comparing assert_metrics, the rpt_bit_flag, metric_preference,
+   and route_metric field are compared in order, where the first lower
+   value wins.  If all fields are equal, the primary IP address of the
+   router that sourced the Assert message is used as a tie-breaker,
+   with the highest IP address winning.
+*/
+int pim_assert_metric_better(const struct pim_assert_metric *m1,
+			     const struct pim_assert_metric *m2)
+{
+  if (m1->rpt_bit_flag < m2->rpt_bit_flag)
+    return 1;
+  if (m1->rpt_bit_flag > m2->rpt_bit_flag)
+    return 0;
+
+  if (m1->metric_preference < m2->metric_preference)
+    return 1;
+  if (m1->metric_preference > m2->metric_preference)
+    return 0;
+
+  if (m1->route_metric < m2->route_metric)
+    return 1;
+  if (m1->route_metric > m2->route_metric)
+    return 0;
+
+  return ntohl(m1->ip_address.s_addr) > ntohl(m2->ip_address.s_addr);
+}
+
+int pim_assert_metric_match(const struct pim_assert_metric *m1,
+			    const struct pim_assert_metric *m2)
+{
+  if (m1->rpt_bit_flag != m2->rpt_bit_flag)
+    return 0;
+  if (m1->metric_preference != m2->metric_preference)
+    return 0;
+  if (m1->route_metric != m2->route_metric)
+    return 0;
+  
+  return m1->ip_address.s_addr == m2->ip_address.s_addr;
+}
+
+int pim_assert_build_msg(char *pim_msg, int buf_size,
+			 struct interface *ifp,
+			 struct in_addr group_addr,
+			 struct in_addr source_addr,
+			 uint32_t metric_preference,
+			 uint32_t route_metric,
+			 uint32_t rpt_bit_flag)
+{
+  char *buf_pastend = pim_msg + buf_size;
+  char *pim_msg_curr;
+  int pim_msg_size;
+  int remain;
+
+  pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* skip room for pim header */
+
+  /* Encode group */
+  remain = buf_pastend - pim_msg_curr;
+  pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
+						remain,
+						group_addr);
+  if (!pim_msg_curr) {
+    char group_str[100];
+    pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+    zlog_warn("%s: failure encoding group address %s: space left=%d",
+	      __PRETTY_FUNCTION__, group_str, remain);
+    return -1;
+  }
+
+  /* Encode source */
+  remain = buf_pastend - pim_msg_curr;
+  pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
+						remain,
+						source_addr);
+  if (!pim_msg_curr) {
+    char source_str[100];
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    zlog_warn("%s: failure encoding source address %s: space left=%d",
+	      __PRETTY_FUNCTION__, source_str, remain);
+    return -2;
+  }
+
+  /* Metric preference */
+  *((uint32_t *) pim_msg_curr) = htonl(rpt_bit_flag ?
+				       metric_preference | 0x80000000 :
+				       metric_preference);
+  pim_msg_curr += 4;
+
+  /* Route metric */
+  *((uint32_t *) pim_msg_curr) = htonl(route_metric);
+  pim_msg_curr += 4;
+
+  /*
+    Add PIM header
+  */
+  pim_msg_size = pim_msg_curr - pim_msg;
+  pim_msg_build_header(pim_msg, pim_msg_size,
+		       PIM_MSG_TYPE_ASSERT);
+
+  return pim_msg_size;
+}
+
+static int pim_assert_do(struct pim_ifchannel *ch,
+			 struct pim_assert_metric metric)
+{
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+  char pim_msg[1000];
+  int pim_msg_size;
+
+  ifp = ch->interface;
+  zassert(ifp);
+
+  pim_ifp = ifp->info;
+  if (!pim_ifp) {
+    zlog_warn("%s: pim not enabled on interface: %s",
+	      __PRETTY_FUNCTION__, ifp->name);
+    return -1;
+  }
+
+  pim_msg_size = pim_assert_build_msg(pim_msg, sizeof(pim_msg), ifp,
+				      ch->group_addr, ch->source_addr,
+				      metric.metric_preference,
+				      metric.route_metric,
+				      metric.rpt_bit_flag);
+  if (pim_msg_size < 1) {
+    zlog_warn("%s: failure building PIM assert message: msg_size=%d",
+	      __PRETTY_FUNCTION__, pim_msg_size);
+    return -2;
+  }
+
+  /*
+    RFC 4601: 4.3.1.  Sending Hello Messages
+    
+    Thus, if a router needs to send a Join/Prune or Assert message on
+    an interface on which it has not yet sent a Hello message with the
+    currently configured IP address, then it MUST immediately send the
+    relevant Hello message without waiting for the Hello Timer to
+    expire, followed by the Join/Prune or Assert message.
+  */
+  pim_hello_require(ifp);
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    char source_str[100];
+    char group_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, source_str, sizeof(source_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, group_str, sizeof(group_str));
+    zlog_debug("%s: to %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
+	       __PRETTY_FUNCTION__, 
+	       ifp->name, source_str, group_str,
+	       metric.metric_preference,
+	       metric.route_metric,
+	       PIM_FORCE_BOOLEAN(metric.rpt_bit_flag));
+  }
+
+  if (pim_msg_send(pim_ifp->pim_sock_fd,
+		   qpim_all_pim_routers_addr,
+		   pim_msg,
+		   pim_msg_size,
+		   ifp->name)) {
+    zlog_warn("%s: could not send PIM message on interface %s",
+	      __PRETTY_FUNCTION__, ifp->name);
+    return -3;
+  }
+
+  return 0;
+}
+
+int pim_assert_send(struct pim_ifchannel *ch)
+{
+  return pim_assert_do(ch, ch->ifassert_my_metric);
+}
+
+/*
+  RFC 4601: 4.6.4.  AssertCancel Messages
+
+  An AssertCancel(S,G) is an infinite metric assert with the RPT bit
+  set that names S as the source.
+ */
+static int pim_assert_cancel(struct pim_ifchannel *ch)
+{
+  struct pim_assert_metric metric;
+
+  metric.rpt_bit_flag      = 0;
+  metric.metric_preference = PIM_ASSERT_METRIC_PREFERENCE_MAX;
+  metric.route_metric      = PIM_ASSERT_ROUTE_METRIC_MAX;
+  metric.ip_address        = ch->source_addr;
+
+  return pim_assert_do(ch, metric);
+}
+
+static int on_assert_timer(struct thread *t)
+{
+  struct pim_ifchannel *ch;
+  struct interface *ifp;
+
+  zassert(t);
+  ch = THREAD_ARG(t);
+  zassert(ch);
+
+  ifp = ch->interface;
+  zassert(ifp);
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_debug("%s: (S,G)=(%s,%s) timer expired on interface %s",
+	       __PRETTY_FUNCTION__,
+	       src_str, grp_str, ifp->name);
+  }
+
+  ch->t_ifassert_timer = 0;
+
+  switch (ch->ifassert_state) {
+  case PIM_IFASSERT_I_AM_WINNER:
+    zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
+    assert_action_a3(ch);
+    break;
+  case PIM_IFASSERT_I_AM_LOSER:
+    assert_action_a5(ch);
+    break;
+  default:
+    {
+      char source_str[100];
+      char group_str[100];
+      pim_inet4_dump("<src?>", ch->source_addr, source_str, sizeof(source_str));
+      pim_inet4_dump("<grp?>", ch->group_addr, group_str, sizeof(group_str));
+      zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s",
+		__PRETTY_FUNCTION__,
+		source_str, group_str, ch->ifassert_state, ifp->name);
+    }
+  }
+
+  return 0;
+}
+
+static void assert_timer_off(struct pim_ifchannel *ch)
+{
+  struct interface *ifp;
+
+  zassert(ch);
+  ifp = ch->interface;
+  zassert(ifp);
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    if (ch->t_ifassert_timer) {
+      char src_str[100];
+      char grp_str[100];
+      pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+      pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+      zlog_debug("%s: (S,G)=(%s,%s) cancelling timer on interface %s",
+		 __PRETTY_FUNCTION__,
+		 src_str, grp_str, ifp->name);
+    }
+  }
+  THREAD_OFF(ch->t_ifassert_timer);
+  zassert(!ch->t_ifassert_timer);
+}
+
+static void pim_assert_timer_set(struct pim_ifchannel *ch,
+				 int interval)
+{
+  struct interface *ifp;
+
+  zassert(ch);
+  ifp = ch->interface;
+  zassert(ifp);
+
+  assert_timer_off(ch);
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_debug("%s: (S,G)=(%s,%s) starting %u sec timer on interface %s",
+	       __PRETTY_FUNCTION__,
+	       src_str, grp_str, interval, ifp->name);
+  }
+
+  THREAD_TIMER_ON(master, ch->t_ifassert_timer,
+		  on_assert_timer,
+		  ch, interval);
+}
+
+static void pim_assert_timer_reset(struct pim_ifchannel *ch)
+{
+  pim_assert_timer_set(ch, PIM_ASSERT_TIME - PIM_ASSERT_OVERRIDE_INTERVAL);
+}
+
+/*
+  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
+
+  (S,G) Assert State machine Actions
+
+  A1:  Send Assert(S,G).
+  Set Assert Timer to (Assert_Time - Assert_Override_Interval).
+  Store self as AssertWinner(S,G,I).
+  Store spt_assert_metric(S,I) as AssertWinnerMetric(S,G,I).
+*/
+int assert_action_a1(struct pim_ifchannel *ch)
+{
+  struct interface *ifp = ch->interface;
+  struct pim_interface *pim_ifp;
+
+  zassert(ifp);
+
+  pim_ifp = ifp->info;
+  if (!pim_ifp) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_warn("%s: (S,G)=(%s,%s) multicast no enabled on interface %s",
+	      __PRETTY_FUNCTION__,
+	      src_str, grp_str, ifp->name);
+    return -1; /* must return since pim_ifp is used below */
+  }
+
+  /* Switch to I_AM_WINNER before performing action_a3 below */
+  pim_ifassert_winner_set(ch, PIM_IFASSERT_I_AM_WINNER,
+			  pim_ifp->primary_address,
+			  pim_macro_spt_assert_metric(&ch->upstream->rpf,
+						      pim_ifp->primary_address));
+
+  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
+  if (assert_action_a3(ch)) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_warn("%s: (S,G)=(%s,%s) assert_action_a3 failure on interface %s",
+	      __PRETTY_FUNCTION__,
+	      src_str, grp_str, ifp->name);
+    /* warning only */
+  }
+
+  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
+
+  return 0;
+}
+
+/*
+  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
+
+  (S,G) Assert State machine Actions
+
+     A2:  Store new assert winner as AssertWinner(S,G,I) and assert
+          winner metric as AssertWinnerMetric(S,G,I).
+          Set Assert Timer to Assert_Time.
+*/
+static void assert_action_a2(struct pim_ifchannel *ch,
+			     struct pim_assert_metric winner_metric)
+{
+  pim_ifassert_winner_set(ch, PIM_IFASSERT_I_AM_LOSER,
+			  winner_metric.ip_address,
+			  winner_metric);
+  
+  pim_assert_timer_set(ch, PIM_ASSERT_TIME);
+
+  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER);
+}
+
+/*
+  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
+
+  (S,G) Assert State machine Actions
+
+  A3:  Send Assert(S,G).
+  Set Assert Timer to (Assert_Time - Assert_Override_Interval).
+*/
+static int assert_action_a3(struct pim_ifchannel *ch)
+{
+  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
+
+  pim_assert_timer_reset(ch);
+
+  if (pim_assert_send(ch)) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+
+    zlog_warn("%s: (S,G)=(%s,%s) failure sending assert on interface %s",
+	      __PRETTY_FUNCTION__,
+	      src_str, grp_str, ch->interface->name);
+    return -1;
+  }
+
+  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
+
+  return 0;
+}
+
+/*
+  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
+
+  (S,G) Assert State machine Actions
+
+     A4:  Send AssertCancel(S,G).
+          Delete assert info (AssertWinner(S,G,I) and
+          AssertWinnerMetric(S,G,I) will then return their default
+          values).
+*/
+void assert_action_a4(struct pim_ifchannel *ch)
+{
+  if (pim_assert_cancel(ch)) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_warn("%s: failure sending AssertCancel(%s,%s) on interface %s",
+	      __PRETTY_FUNCTION__,
+	      src_str, grp_str, ch->interface->name);
+    /* log warning only */
+  }
+
+  assert_action_a5(ch);
+
+  zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO);
+}
+
+/*
+  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
+
+  (S,G) Assert State machine Actions
+
+  A5: Delete assert info (AssertWinner(S,G,I) and
+  AssertWinnerMetric(S,G,I) will then return their default values).
+*/
+void assert_action_a5(struct pim_ifchannel *ch)
+{
+  reset_ifassert_state(ch);
+  zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO);
+}
+
+/*
+  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
+
+  (S,G) Assert State machine Actions
+
+     A6:  Store new assert winner as AssertWinner(S,G,I) and assert
+          winner metric as AssertWinnerMetric(S,G,I).
+          Set Assert Timer to Assert_Time.
+          If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true)
+          set SPTbit(S,G) to TRUE.
+*/
+static void assert_action_a6(struct pim_ifchannel *ch,
+			     struct pim_assert_metric winner_metric)
+{
+  assert_action_a2(ch, winner_metric);
+
+  /*
+    If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) set
+    SPTbit(S,G) to TRUE.
+    
+    Notice: For PIM SSM, SPTbit(S,G) is already always true.
+  */
+
+  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER);
+}
+