New upstream release and new maintainer
[quagga-debian.git] / pimd / pim_assert.c
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
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);
46
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)
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
98 static 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
109 static 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
116 static 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
123 static 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
130 static 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
137 static 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
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)
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
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)
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 */
352 int 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
373 int 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
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)
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
447 static 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
513 int 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  */
524 static 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
536 static 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
583 static 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
606 static 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
632 static 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 */
647 int 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 */
698 static 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 */
718 static 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 */
751 void 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 */
777 void 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 */
794 static 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