1/*
2  PIM for Quagga
3  Copyright (C) 2008  Everton da Silva Marques
4
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.
9
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.
14
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,
18  MA 02110-1301 USA
19
20  $QuaggaId: $Format:%an, %ai, %h$ $
21*/
22
23#include <zebra.h>
24
25#include "log.h"
26#include "prefix.h"
27
28#include "pimd.h"
29#include "pim_str.h"
30#include "pim_tlv.h"
31#include "pim_msg.h"
32#include "pim_pim.h"
33#include "pim_int.h"
34#include "pim_time.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"
40
41static int assert_action_a3(struct pim_ifchannel *ch);
42static void assert_action_a2(struct pim_ifchannel *ch,
43			     struct pim_assert_metric winner_metric);
44static void assert_action_a6(struct pim_ifchannel *ch,
45			     struct pim_assert_metric winner_metric);
46
47void 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)
51{
52  int winner_changed = (ch->ifassert_winner.s_addr != winner.s_addr);
53  int metric_changed = !pim_assert_metric_match(&ch->ifassert_winner_metric,
54						&winner_metric);
55
56  if (PIM_DEBUG_PIM_EVENTS) {
57    if (ch->ifassert_state != new_state) {
58      char src_str[100];
59      char grp_str[100];
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",
63		__PRETTY_FUNCTION__,
64		src_str, grp_str,
65		pim_ifchannel_ifassert_name(ch->ifassert_state),
66		pim_ifchannel_ifassert_name(new_state),
67		ch->interface->name);
68    }
69
70    if (winner_changed) {
71      char src_str[100];
72      char grp_str[100];
73      char was_str[100];
74      char winner_str[100];
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",
80		__PRETTY_FUNCTION__,
81		src_str, grp_str,
82		was_str, winner_str, ch->interface->name);
83    }
84  } /* PIM_DEBUG_PIM_EVENTS */
85
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();
90
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);
95  }
96}
97
98static void on_trace(const char *label,
99		     struct interface *ifp, struct in_addr src)
100{
101  if (PIM_DEBUG_PIM_TRACE) {
102    char src_str[100];
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);
106  }
107}
108
109static int preferred_assert(const struct pim_ifchannel *ch,
110			    const struct pim_assert_metric *recv_metric)
111{
112  return pim_assert_metric_better(recv_metric,
113				  &ch->ifassert_winner_metric);
114}
115
116static int acceptable_assert(const struct pim_assert_metric *my_metric,
117			     const struct pim_assert_metric *recv_metric)
118{
119  return pim_assert_metric_better(recv_metric,
120				  my_metric);
121}
122
123static int inferior_assert(const struct pim_assert_metric *my_metric,
124			   const struct pim_assert_metric *recv_metric)
125{
126  return pim_assert_metric_better(my_metric,
127				  recv_metric);
128}
129
130static int cancel_assert(const struct pim_assert_metric *recv_metric)
131{
132  return (recv_metric->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX)
133    &&
134    (recv_metric->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX);
135}
136
137static void if_could_assert_do_a1(const char *caller,
138				  struct pim_ifchannel *ch)
139{
140  if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
141    if (assert_action_a1(ch)) {
142      char src_str[100];
143      char grp_str[100];
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 */
150    }
151  }
152}
153
154static 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)
158{
159  struct pim_ifchannel *ch;
160
161  ch = pim_ifchannel_add(ifp, source_addr, group_addr);
162  if (!ch) {
163    char source_str[100];
164    char group_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",
168	      __PRETTY_FUNCTION__,
169	      source_str, group_str, ifp->name);
170    return -1;
171  }
172
173  switch (ch->ifassert_state) {
174  case PIM_IFASSERT_NOINFO:
175    if (recv_metric.rpt_bit_flag) {
176      /* RPT bit set */
177      if_could_assert_do_a1(__PRETTY_FUNCTION__, ch);
178    }
179    else {
180      /* RPT bit clear */
181      if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
182	if_could_assert_do_a1(__PRETTY_FUNCTION__, ch);
183      }
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);
187	}
188      }
189    }
190    break;
191  case PIM_IFASSERT_I_AM_WINNER:
192    if (preferred_assert(ch, &recv_metric)) {
193      assert_action_a2(ch, recv_metric);
194    }
195    else {
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);
199      }
200    }
201    break;
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 */
205
206      if (cancel_assert(&recv_metric)) {
207	assert_action_a5(ch);
208      }
209      else {
210	if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
211	  assert_action_a5(ch);
212	}
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);
216	  }
217	}
218      }
219    }
220    else if (preferred_assert(ch, &recv_metric)) {
221      assert_action_a2(ch, recv_metric);
222    }
223    break;
224  default:
225    {
226      char source_str[100];
227      char group_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",
231		__PRETTY_FUNCTION__,
232		source_str, group_str, ch->ifassert_state, ifp->name);
233    }
234    return -2;
235  }
236
237  return 0;
238}
239
240int pim_assert_recv(struct interface *ifp,
241		    struct pim_neighbor *neigh,
242		    struct in_addr src_addr,
243		    uint8_t *buf, int buf_size)
244{
245  struct prefix            msg_group_addr;
246  struct prefix            msg_source_addr;
247  struct pim_assert_metric msg_metric;
248  int offset;
249  uint8_t *curr;
250  int curr_size;
251
252  on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
253
254  curr      = buf;
255  curr_size = buf_size;
256
257  /*
258    Parse assert group addr
259   */
260  offset = pim_parse_addr_group(ifp->name, src_addr,
261				&msg_group_addr,
262				curr, curr_size);
263  if (offset < 1) {
264    char src_str[100];
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",
267	      __PRETTY_FUNCTION__,
268	      src_str, ifp->name);
269    return -1;
270  }
271  curr      += offset;
272  curr_size -= offset;
273
274  /*
275    Parse assert source addr
276  */
277  offset = pim_parse_addr_ucast(ifp->name, src_addr,
278				&msg_source_addr,
279				curr, curr_size);
280  if (offset < 1) {
281    char src_str[100];
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",
284	      __PRETTY_FUNCTION__,
285	      src_str, ifp->name);
286    return -2;
287  }
288  curr      += offset;
289  curr_size -= offset;
290
291  if (curr_size != 8) {
292    char src_str[100];
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",
295	      __PRETTY_FUNCTION__,
296	      curr_size,
297	      src_str, ifp->name);
298    return -3;
299  }
300
301  /*
302    Parse assert metric preference
303  */
304
305  msg_metric.metric_preference = pim_read_uint32_host(curr);
306
307  msg_metric.rpt_bit_flag = msg_metric.metric_preference & 0x80000000; /* save highest bit */
308  msg_metric.metric_preference &= ~0x80000000; /* clear highest bit */
309
310  curr += 4;
311
312  /*
313    Parse assert route metric
314  */
315
316  msg_metric.route_metric = pim_read_uint32_host(curr);
317
318  if (PIM_DEBUG_PIM_TRACE) {
319    char neigh_str[100];
320    char source_str[100];
321    char group_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));
331  }
332
333  msg_metric.ip_address = src_addr;
334
335  return dispatch_assert(ifp,
336			 msg_source_addr.u.prefix4,
337			 msg_group_addr.u.prefix4,
338			 msg_metric);
339}
340
341/*
342  RFC 4601: 4.6.3.  Assert Metrics
343
344   Assert metrics are defined as:
345
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.
351*/
352int pim_assert_metric_better(const struct pim_assert_metric *m1,
353			     const struct pim_assert_metric *m2)
354{
355  if (m1->rpt_bit_flag < m2->rpt_bit_flag)
356    return 1;
357  if (m1->rpt_bit_flag > m2->rpt_bit_flag)
358    return 0;
359
360  if (m1->metric_preference < m2->metric_preference)
361    return 1;
362  if (m1->metric_preference > m2->metric_preference)
363    return 0;
364
365  if (m1->route_metric < m2->route_metric)
366    return 1;
367  if (m1->route_metric > m2->route_metric)
368    return 0;
369
370  return ntohl(m1->ip_address.s_addr) > ntohl(m2->ip_address.s_addr);
371}
372
373int pim_assert_metric_match(const struct pim_assert_metric *m1,
374			    const struct pim_assert_metric *m2)
375{
376  if (m1->rpt_bit_flag != m2->rpt_bit_flag)
377    return 0;
378  if (m1->metric_preference != m2->metric_preference)
379    return 0;
380  if (m1->route_metric != m2->route_metric)
381    return 0;
382
383  return m1->ip_address.s_addr == m2->ip_address.s_addr;
384}
385
386int 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)
393{
394  uint8_t *buf_pastend = pim_msg + buf_size;
395  uint8_t *pim_msg_curr;
396  int pim_msg_size;
397  int remain;
398
399  pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* skip room for pim header */
400
401  /* Encode group */
402  remain = buf_pastend - pim_msg_curr;
403  pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
404						remain,
405						group_addr);
406  if (!pim_msg_curr) {
407    char group_str[100];
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);
411    return -1;
412  }
413
414  /* Encode source */
415  remain = buf_pastend - pim_msg_curr;
416  pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
417						remain,
418						source_addr);
419  if (!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);
424    return -2;
425  }
426
427  /* Metric preference */
428  pim_write_uint32(pim_msg_curr, rpt_bit_flag ?
429		   metric_preference | 0x80000000 :
430		   metric_preference);
431  pim_msg_curr += 4;
432
433  /* Route metric */
434  pim_write_uint32(pim_msg_curr, route_metric);
435  pim_msg_curr += 4;
436
437  /*
438    Add PIM header
439  */
440  pim_msg_size = pim_msg_curr - pim_msg;
441  pim_msg_build_header(pim_msg, pim_msg_size,
442		       PIM_MSG_TYPE_ASSERT);
443
444  return pim_msg_size;
445}
446
447static int pim_assert_do(struct pim_ifchannel *ch,
448			 struct pim_assert_metric metric)
449{
450  struct interface *ifp;
451  struct pim_interface *pim_ifp;
452  uint8_t pim_msg[1000];
453  int pim_msg_size;
454
455  ifp = ch->interface;
456  zassert(ifp);
457
458  pim_ifp = ifp->info;
459  if (!pim_ifp) {
460    zlog_warn("%s: pim not enabled on interface: %s",
461	      __PRETTY_FUNCTION__, ifp->name);
462    return -1;
463  }
464
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,
468				      metric.route_metric,
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);
473    return -2;
474  }
475
476  /*
477    RFC 4601: 4.3.1.  Sending Hello Messages
478
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.
484  */
485  pim_hello_require(ifp);
486
487  if (PIM_DEBUG_PIM_TRACE) {
488    char source_str[100];
489    char group_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",
493	       __PRETTY_FUNCTION__,
494	       ifp->name, source_str, group_str,
495	       metric.metric_preference,
496	       metric.route_metric,
497	       PIM_FORCE_BOOLEAN(metric.rpt_bit_flag));
498  }
499
500  if (pim_msg_send(pim_ifp->pim_sock_fd,
501		   qpim_all_pim_routers_addr,
502		   pim_msg,
503		   pim_msg_size,
504		   ifp->name)) {
505    zlog_warn("%s: could not send PIM message on interface %s",
506	      __PRETTY_FUNCTION__, ifp->name);
507    return -3;
508  }
509
510  return 0;
511}
512
513int pim_assert_send(struct pim_ifchannel *ch)
514{
515  return pim_assert_do(ch, ch->ifassert_my_metric);
516}
517
518/*
519  RFC 4601: 4.6.4.  AssertCancel Messages
520
521  An AssertCancel(S,G) is an infinite metric assert with the RPT bit
522  set that names S as the source.
523 */
524static int pim_assert_cancel(struct pim_ifchannel *ch)
525{
526  struct pim_assert_metric metric;
527
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;
532
533  return pim_assert_do(ch, metric);
534}
535
536static int on_assert_timer(struct thread *t)
537{
538  struct pim_ifchannel *ch;
539  struct interface *ifp;
540
541  zassert(t);
542  ch = THREAD_ARG(t);
543  zassert(ch);
544
545  ifp = ch->interface;
546  zassert(ifp);
547
548  if (PIM_DEBUG_PIM_TRACE) {
549    char src_str[100];
550    char grp_str[100];
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",
554	       __PRETTY_FUNCTION__,
555	       src_str, grp_str, ifp->name);
556  }
557
558  ch->t_ifassert_timer = 0;
559
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);
564    break;
565  case PIM_IFASSERT_I_AM_LOSER:
566    assert_action_a5(ch);
567    break;
568  default:
569    {
570      char source_str[100];
571      char group_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",
575		__PRETTY_FUNCTION__,
576		source_str, group_str, ch->ifassert_state, ifp->name);
577    }
578  }
579
580  return 0;
581}
582
583static void assert_timer_off(struct pim_ifchannel *ch)
584{
585  struct interface *ifp;
586
587  zassert(ch);
588  ifp = ch->interface;
589  zassert(ifp);
590
591  if (PIM_DEBUG_PIM_TRACE) {
592    if (ch->t_ifassert_timer) {
593      char src_str[100];
594      char grp_str[100];
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",
598		 __PRETTY_FUNCTION__,
599		 src_str, grp_str, ifp->name);
600    }
601  }
602  THREAD_OFF(ch->t_ifassert_timer);
603  zassert(!ch->t_ifassert_timer);
604}
605
606static void pim_assert_timer_set(struct pim_ifchannel *ch,
607				 int interval)
608{
609  struct interface *ifp;
610
611  zassert(ch);
612  ifp = ch->interface;
613  zassert(ifp);
614
615  assert_timer_off(ch);
616
617  if (PIM_DEBUG_PIM_TRACE) {
618    char src_str[100];
619    char grp_str[100];
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",
623	       __PRETTY_FUNCTION__,
624	       src_str, grp_str, interval, ifp->name);
625  }
626
627  THREAD_TIMER_ON(master, ch->t_ifassert_timer,
628		  on_assert_timer,
629		  ch, interval);
630}
631
632static void pim_assert_timer_reset(struct pim_ifchannel *ch)
633{
634  pim_assert_timer_set(ch, PIM_ASSERT_TIME - PIM_ASSERT_OVERRIDE_INTERVAL);
635}
636
637/*
638  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
639
640  (S,G) Assert State machine Actions
641
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).
646*/
647int assert_action_a1(struct pim_ifchannel *ch)
648{
649  struct interface *ifp = ch->interface;
650  struct pim_interface *pim_ifp;
651
652  zassert(ifp);
653
654  pim_ifp = ifp->info;
655  if (!pim_ifp) {
656    char src_str[100];
657    char grp_str[100];
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",
661	      __PRETTY_FUNCTION__,
662	      src_str, grp_str, ifp->name);
663    return -1; /* must return since pim_ifp is used below */
664  }
665
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));
671
672  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
673  if (assert_action_a3(ch)) {
674    char src_str[100];
675    char grp_str[100];
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",
679	      __PRETTY_FUNCTION__,
680	      src_str, grp_str, ifp->name);
681    /* warning only */
682  }
683
684  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
685
686  return 0;
687}
688
689/*
690  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
691
692  (S,G) Assert State machine Actions
693
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.
697*/
698static void assert_action_a2(struct pim_ifchannel *ch,
699			     struct pim_assert_metric winner_metric)
700{
701  pim_ifassert_winner_set(ch, PIM_IFASSERT_I_AM_LOSER,
702			  winner_metric.ip_address,
703			  winner_metric);
704
705  pim_assert_timer_set(ch, PIM_ASSERT_TIME);
706
707  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER);
708}
709
710/*
711  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
712
713  (S,G) Assert State machine Actions
714
715  A3:  Send Assert(S,G).
716  Set Assert Timer to (Assert_Time - Assert_Override_Interval).
717*/
718static int assert_action_a3(struct pim_ifchannel *ch)
719{
720  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
721
722  pim_assert_timer_reset(ch);
723
724  if (pim_assert_send(ch)) {
725    char src_str[100];
726    char grp_str[100];
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));
729
730    zlog_warn("%s: (S,G)=(%s,%s) failure sending assert on interface %s",
731	      __PRETTY_FUNCTION__,
732	      src_str, grp_str, ch->interface->name);
733    return -1;
734  }
735
736  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
737
738  return 0;
739}
740
741/*
742  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
743
744  (S,G) Assert State machine Actions
745
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
749          values).
750*/
751void assert_action_a4(struct pim_ifchannel *ch)
752{
753  if (pim_assert_cancel(ch)) {
754    char src_str[100];
755    char grp_str[100];
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",
759	      __PRETTY_FUNCTION__,
760	      src_str, grp_str, ch->interface->name);
761    /* log warning only */
762  }
763
764  assert_action_a5(ch);
765
766  zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO);
767}
768
769/*
770  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
771
772  (S,G) Assert State machine Actions
773
774  A5: Delete assert info (AssertWinner(S,G,I) and
775  AssertWinnerMetric(S,G,I) will then return their default values).
776*/
777void assert_action_a5(struct pim_ifchannel *ch)
778{
779  reset_ifassert_state(ch);
780  zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO);
781}
782
783/*
784  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
785
786  (S,G) Assert State machine Actions
787
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.
793*/
794static void assert_action_a6(struct pim_ifchannel *ch,
795			     struct pim_assert_metric winner_metric)
796{
797  assert_action_a2(ch, winner_metric);
798
799  /*
800    If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) set
801    SPTbit(S,G) to TRUE.
802
803    Notice: For PIM SSM, SPTbit(S,G) is already always true.
804  */
805
806  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER);
807}
808
809