blob: 0ce0e5d0d3abe09a8418892591d178e06628c27c [file] [log] [blame]
Everton Marques871dbcf2009-08-11 15:43:05 -03001/*
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_time.h"
34#include "pim_iface.h"
35#include "pim_hello.h"
36#include "pim_macro.h"
37#include "pim_assert.h"
38#include "pim_ifchannel.h"
39
40static int assert_action_a3(struct pim_ifchannel *ch);
41static void assert_action_a2(struct pim_ifchannel *ch,
42 struct pim_assert_metric winner_metric);
43static void assert_action_a6(struct pim_ifchannel *ch,
44 struct pim_assert_metric winner_metric);
45
46void pim_ifassert_winner_set(struct pim_ifchannel *ch,
47 enum pim_ifassert_state new_state,
48 struct in_addr winner,
49 struct pim_assert_metric winner_metric)
50{
51 int winner_changed = (ch->ifassert_winner.s_addr != winner.s_addr);
52 int metric_changed = !pim_assert_metric_match(&ch->ifassert_winner_metric,
53 &winner_metric);
54
55 if (ch->ifassert_state != new_state) {
56 char src_str[100];
57 char grp_str[100];
58 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
59 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
60 zlog_info("%s: (S,G)=(%s,%s) assert state changed from %s to %s on interface %s",
61 __PRETTY_FUNCTION__,
62 src_str, grp_str,
63 pim_ifchannel_ifassert_name(ch->ifassert_state),
64 pim_ifchannel_ifassert_name(new_state),
65 ch->interface->name);
66 }
67
68 {
69 char src_str[100];
70 char grp_str[100];
71 char winner_str[100];
72 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
73 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
74 pim_inet4_dump("<winner?>", winner, winner_str, sizeof(winner_str));
75 zlog_info("%s: (S,G)=(%s,%s) assert winner now is %s on interface %s",
76 __PRETTY_FUNCTION__,
77 src_str, grp_str,
78 winner_str, ch->interface->name);
79 }
80
81 ch->ifassert_state = new_state;
82 ch->ifassert_winner = winner;
83 ch->ifassert_winner_metric = winner_metric;
84 ch->ifassert_creation = pim_time_monotonic_sec();
85
86 if (winner_changed || metric_changed) {
87 pim_upstream_update_join_desired(ch->upstream);
88 pim_ifchannel_update_could_assert(ch);
89 pim_ifchannel_update_assert_tracking_desired(ch);
90 }
91}
92
93static void on_trace(const char *label,
94 struct interface *ifp, struct in_addr src)
95{
96 if (PIM_DEBUG_PIM_TRACE) {
97 char src_str[100];
98 pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
99 zlog_debug("%s: from %s on %s",
100 label, src_str, ifp->name);
101 }
102}
103
104static int preferred_assert(const struct pim_ifchannel *ch,
105 const struct pim_assert_metric *recv_metric)
106{
107 return pim_assert_metric_better(recv_metric,
108 &ch->ifassert_winner_metric);
109}
110
111static int acceptable_assert(const struct pim_assert_metric *my_metric,
112 const struct pim_assert_metric *recv_metric)
113{
114 return pim_assert_metric_better(recv_metric,
115 my_metric);
116}
117
118static int inferior_assert(const struct pim_assert_metric *my_metric,
119 const struct pim_assert_metric *recv_metric)
120{
121 return pim_assert_metric_better(my_metric,
122 recv_metric);
123}
124
125static int cancel_assert(const struct pim_assert_metric *recv_metric)
126{
127 return (recv_metric->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX)
128 &&
129 (recv_metric->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX);
130}
131
132static void if_could_assert_do_a1(const char *caller,
133 struct pim_ifchannel *ch)
134{
135 if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
136 if (assert_action_a1(ch)) {
137 char src_str[100];
138 char grp_str[100];
139 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
140 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
141 zlog_warn("%s: %s: (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
142 __PRETTY_FUNCTION__, caller,
143 src_str, grp_str, ch->interface->name);
144 /* log warning only */
145 }
146 }
147}
148
149static int dispatch_assert(struct interface *ifp,
150 struct in_addr source_addr,
151 struct in_addr group_addr,
152 struct pim_assert_metric recv_metric)
153{
154 struct pim_ifchannel *ch;
155
156 ch = pim_ifchannel_add(ifp, source_addr, group_addr);
157 if (!ch) {
158 char source_str[100];
159 char group_str[100];
160 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
161 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
162 zlog_warn("%s: (S,G)=(%s,%s) failure creating channel on interface %s",
163 __PRETTY_FUNCTION__,
164 source_str, group_str, ifp->name);
165 return -1;
166 }
167
168 switch (ch->ifassert_state) {
169 case PIM_IFASSERT_NOINFO:
170 if (recv_metric.rpt_bit_flag) {
171 /* RPT bit set */
172 if_could_assert_do_a1(__PRETTY_FUNCTION__, ch);
173 }
174 else {
175 /* RPT bit clear */
176 if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
177 if_could_assert_do_a1(__PRETTY_FUNCTION__, ch);
178 }
179 else if (acceptable_assert(&ch->ifassert_my_metric, &recv_metric)) {
180 if (PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags)) {
181 assert_action_a6(ch, recv_metric);
182 }
183 }
184 }
185 break;
186 case PIM_IFASSERT_I_AM_WINNER:
187 if (preferred_assert(ch, &recv_metric)) {
188 assert_action_a2(ch, recv_metric);
189 }
190 else {
191 if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
192 zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
193 assert_action_a3(ch);
194 }
195 }
196 break;
197 case PIM_IFASSERT_I_AM_LOSER:
198 if (recv_metric.ip_address.s_addr == ch->ifassert_winner.s_addr) {
199 /* Assert from current winner */
200
201 if (cancel_assert(&recv_metric)) {
202 assert_action_a5(ch);
203 }
204 else {
205 if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
206 assert_action_a5(ch);
207 }
208 else if (acceptable_assert(&ch->ifassert_my_metric, &recv_metric)) {
209 if (!recv_metric.rpt_bit_flag) {
210 assert_action_a2(ch, recv_metric);
211 }
212 }
213 }
214 }
215 else if (preferred_assert(ch, &recv_metric)) {
216 assert_action_a2(ch, recv_metric);
217 }
218 break;
219 default:
220 {
221 char source_str[100];
222 char group_str[100];
223 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
224 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
225 zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s",
226 __PRETTY_FUNCTION__,
227 source_str, group_str, ch->ifassert_state, ifp->name);
228 }
229 return -2;
230 }
231
232 return 0;
233}
234
235int pim_assert_recv(struct interface *ifp,
236 struct pim_neighbor *neigh,
237 struct in_addr src_addr,
238 char *buf, int buf_size)
239{
240 struct prefix msg_group_addr;
241 struct prefix msg_source_addr;
242 struct pim_assert_metric msg_metric;
243 int offset;
244 char *curr;
245 int curr_size;
246
247 on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
248
249 curr = buf;
250 curr_size = buf_size;
251
252 /*
253 Parse assert group addr
254 */
255 offset = pim_parse_addr_group(ifp->name, src_addr,
256 &msg_group_addr,
257 curr, curr_size);
258 if (offset < 1) {
259 char src_str[100];
260 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
261 zlog_warn("%s: pim_parse_addr_group() failure: from %s on %s",
262 __PRETTY_FUNCTION__,
263 src_str, ifp->name);
264 return -1;
265 }
266 curr += offset;
267 curr_size -= offset;
268
269 /*
270 Parse assert source addr
271 */
272 offset = pim_parse_addr_ucast(ifp->name, src_addr,
273 &msg_source_addr,
274 curr, curr_size);
275 if (offset < 1) {
276 char src_str[100];
277 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
278 zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
279 __PRETTY_FUNCTION__,
280 src_str, ifp->name);
281 return -2;
282 }
283 curr += offset;
284 curr_size -= offset;
285
286 if (curr_size != 8) {
287 char src_str[100];
288 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
289 zlog_warn("%s: preference/metric size is not 8: size=%d from %s on interface %s",
290 __PRETTY_FUNCTION__,
291 curr_size,
292 src_str, ifp->name);
293 return -3;
294 }
295
296 /*
297 Parse assert metric preference
298 */
299
300 msg_metric.metric_preference = ntohl(*(const uint32_t *) curr);
301
302 msg_metric.rpt_bit_flag = msg_metric.metric_preference & 0x80000000; /* save highest bit */
303 msg_metric.metric_preference &= ~0x80000000; /* clear highest bit */
304
305 curr += 4;
306
307 /*
308 Parse assert route metric
309 */
310
311 msg_metric.route_metric = ntohl(*(const uint32_t *) curr);
312
313 if (PIM_DEBUG_PIM_TRACE) {
314 char neigh_str[100];
315 char source_str[100];
316 char group_str[100];
317 pim_inet4_dump("<neigh?>", src_addr, neigh_str, sizeof(neigh_str));
318 pim_inet4_dump("<src?>", msg_source_addr.u.prefix4, source_str, sizeof(source_str));
319 pim_inet4_dump("<grp?>", msg_group_addr.u.prefix4, group_str, sizeof(group_str));
320 zlog_debug("%s: from %s on %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
321 __PRETTY_FUNCTION__, neigh_str, ifp->name,
322 source_str, group_str,
323 msg_metric.metric_preference,
324 msg_metric.route_metric,
325 PIM_FORCE_BOOLEAN(msg_metric.rpt_bit_flag));
326 }
327
328 msg_metric.ip_address = src_addr;
329
330 return dispatch_assert(ifp,
331 msg_source_addr.u.prefix4,
332 msg_group_addr.u.prefix4,
333 msg_metric);
334}
335
336/*
337 RFC 4601: 4.6.3. Assert Metrics
338
339 Assert metrics are defined as:
340
341 When comparing assert_metrics, the rpt_bit_flag, metric_preference,
342 and route_metric field are compared in order, where the first lower
343 value wins. If all fields are equal, the primary IP address of the
344 router that sourced the Assert message is used as a tie-breaker,
345 with the highest IP address winning.
346*/
347int pim_assert_metric_better(const struct pim_assert_metric *m1,
348 const struct pim_assert_metric *m2)
349{
350 if (m1->rpt_bit_flag < m2->rpt_bit_flag)
351 return 1;
352 if (m1->rpt_bit_flag > m2->rpt_bit_flag)
353 return 0;
354
355 if (m1->metric_preference < m2->metric_preference)
356 return 1;
357 if (m1->metric_preference > m2->metric_preference)
358 return 0;
359
360 if (m1->route_metric < m2->route_metric)
361 return 1;
362 if (m1->route_metric > m2->route_metric)
363 return 0;
364
365 return ntohl(m1->ip_address.s_addr) > ntohl(m2->ip_address.s_addr);
366}
367
368int pim_assert_metric_match(const struct pim_assert_metric *m1,
369 const struct pim_assert_metric *m2)
370{
371 if (m1->rpt_bit_flag != m2->rpt_bit_flag)
372 return 0;
373 if (m1->metric_preference != m2->metric_preference)
374 return 0;
375 if (m1->route_metric != m2->route_metric)
376 return 0;
377
378 return m1->ip_address.s_addr == m2->ip_address.s_addr;
379}
380
381int pim_assert_build_msg(char *pim_msg, int buf_size,
382 struct interface *ifp,
383 struct in_addr group_addr,
384 struct in_addr source_addr,
385 uint32_t metric_preference,
386 uint32_t route_metric,
387 uint32_t rpt_bit_flag)
388{
389 char *buf_pastend = pim_msg + buf_size;
390 char *pim_msg_curr;
391 int pim_msg_size;
392 int remain;
393
394 pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* skip room for pim header */
395
396 /* Encode group */
397 remain = buf_pastend - pim_msg_curr;
398 pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
399 remain,
400 group_addr);
401 if (!pim_msg_curr) {
402 char group_str[100];
403 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
404 zlog_warn("%s: failure encoding group address %s: space left=%d",
405 __PRETTY_FUNCTION__, group_str, remain);
406 return -1;
407 }
408
409 /* Encode source */
410 remain = buf_pastend - pim_msg_curr;
411 pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
412 remain,
413 source_addr);
414 if (!pim_msg_curr) {
415 char source_str[100];
416 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
417 zlog_warn("%s: failure encoding source address %s: space left=%d",
418 __PRETTY_FUNCTION__, source_str, remain);
419 return -2;
420 }
421
422 /* Metric preference */
423 *((uint32_t *) pim_msg_curr) = htonl(rpt_bit_flag ?
424 metric_preference | 0x80000000 :
425 metric_preference);
426 pim_msg_curr += 4;
427
428 /* Route metric */
429 *((uint32_t *) pim_msg_curr) = htonl(route_metric);
430 pim_msg_curr += 4;
431
432 /*
433 Add PIM header
434 */
435 pim_msg_size = pim_msg_curr - pim_msg;
436 pim_msg_build_header(pim_msg, pim_msg_size,
437 PIM_MSG_TYPE_ASSERT);
438
439 return pim_msg_size;
440}
441
442static int pim_assert_do(struct pim_ifchannel *ch,
443 struct pim_assert_metric metric)
444{
445 struct interface *ifp;
446 struct pim_interface *pim_ifp;
447 char pim_msg[1000];
448 int pim_msg_size;
449
450 ifp = ch->interface;
451 zassert(ifp);
452
453 pim_ifp = ifp->info;
454 if (!pim_ifp) {
455 zlog_warn("%s: pim not enabled on interface: %s",
456 __PRETTY_FUNCTION__, ifp->name);
457 return -1;
458 }
459
460 pim_msg_size = pim_assert_build_msg(pim_msg, sizeof(pim_msg), ifp,
461 ch->group_addr, ch->source_addr,
462 metric.metric_preference,
463 metric.route_metric,
464 metric.rpt_bit_flag);
465 if (pim_msg_size < 1) {
466 zlog_warn("%s: failure building PIM assert message: msg_size=%d",
467 __PRETTY_FUNCTION__, pim_msg_size);
468 return -2;
469 }
470
471 /*
472 RFC 4601: 4.3.1. Sending Hello Messages
473
474 Thus, if a router needs to send a Join/Prune or Assert message on
475 an interface on which it has not yet sent a Hello message with the
476 currently configured IP address, then it MUST immediately send the
477 relevant Hello message without waiting for the Hello Timer to
478 expire, followed by the Join/Prune or Assert message.
479 */
480 pim_hello_require(ifp);
481
482 if (PIM_DEBUG_PIM_TRACE) {
483 char source_str[100];
484 char group_str[100];
485 pim_inet4_dump("<src?>", ch->source_addr, source_str, sizeof(source_str));
486 pim_inet4_dump("<grp?>", ch->group_addr, group_str, sizeof(group_str));
487 zlog_debug("%s: to %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
488 __PRETTY_FUNCTION__,
489 ifp->name, source_str, group_str,
490 metric.metric_preference,
491 metric.route_metric,
492 PIM_FORCE_BOOLEAN(metric.rpt_bit_flag));
493 }
494
495 if (pim_msg_send(pim_ifp->pim_sock_fd,
496 qpim_all_pim_routers_addr,
497 pim_msg,
498 pim_msg_size,
499 ifp->name)) {
500 zlog_warn("%s: could not send PIM message on interface %s",
501 __PRETTY_FUNCTION__, ifp->name);
502 return -3;
503 }
504
505 return 0;
506}
507
508int pim_assert_send(struct pim_ifchannel *ch)
509{
510 return pim_assert_do(ch, ch->ifassert_my_metric);
511}
512
513/*
514 RFC 4601: 4.6.4. AssertCancel Messages
515
516 An AssertCancel(S,G) is an infinite metric assert with the RPT bit
517 set that names S as the source.
518 */
519static int pim_assert_cancel(struct pim_ifchannel *ch)
520{
521 struct pim_assert_metric metric;
522
523 metric.rpt_bit_flag = 0;
524 metric.metric_preference = PIM_ASSERT_METRIC_PREFERENCE_MAX;
525 metric.route_metric = PIM_ASSERT_ROUTE_METRIC_MAX;
526 metric.ip_address = ch->source_addr;
527
528 return pim_assert_do(ch, metric);
529}
530
531static int on_assert_timer(struct thread *t)
532{
533 struct pim_ifchannel *ch;
534 struct interface *ifp;
535
536 zassert(t);
537 ch = THREAD_ARG(t);
538 zassert(ch);
539
540 ifp = ch->interface;
541 zassert(ifp);
542
543 if (PIM_DEBUG_PIM_TRACE) {
544 char src_str[100];
545 char grp_str[100];
546 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
547 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
548 zlog_debug("%s: (S,G)=(%s,%s) timer expired on interface %s",
549 __PRETTY_FUNCTION__,
550 src_str, grp_str, ifp->name);
551 }
552
553 ch->t_ifassert_timer = 0;
554
555 switch (ch->ifassert_state) {
556 case PIM_IFASSERT_I_AM_WINNER:
557 zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
558 assert_action_a3(ch);
559 break;
560 case PIM_IFASSERT_I_AM_LOSER:
561 assert_action_a5(ch);
562 break;
563 default:
564 {
565 char source_str[100];
566 char group_str[100];
567 pim_inet4_dump("<src?>", ch->source_addr, source_str, sizeof(source_str));
568 pim_inet4_dump("<grp?>", ch->group_addr, group_str, sizeof(group_str));
569 zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s",
570 __PRETTY_FUNCTION__,
571 source_str, group_str, ch->ifassert_state, ifp->name);
572 }
573 }
574
575 return 0;
576}
577
578static void assert_timer_off(struct pim_ifchannel *ch)
579{
580 struct interface *ifp;
581
582 zassert(ch);
583 ifp = ch->interface;
584 zassert(ifp);
585
586 if (PIM_DEBUG_PIM_TRACE) {
587 if (ch->t_ifassert_timer) {
588 char src_str[100];
589 char grp_str[100];
590 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
591 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
592 zlog_debug("%s: (S,G)=(%s,%s) cancelling timer on interface %s",
593 __PRETTY_FUNCTION__,
594 src_str, grp_str, ifp->name);
595 }
596 }
597 THREAD_OFF(ch->t_ifassert_timer);
598 zassert(!ch->t_ifassert_timer);
599}
600
601static void pim_assert_timer_set(struct pim_ifchannel *ch,
602 int interval)
603{
604 struct interface *ifp;
605
606 zassert(ch);
607 ifp = ch->interface;
608 zassert(ifp);
609
610 assert_timer_off(ch);
611
612 if (PIM_DEBUG_PIM_TRACE) {
613 char src_str[100];
614 char grp_str[100];
615 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
616 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
617 zlog_debug("%s: (S,G)=(%s,%s) starting %u sec timer on interface %s",
618 __PRETTY_FUNCTION__,
619 src_str, grp_str, interval, ifp->name);
620 }
621
622 THREAD_TIMER_ON(master, ch->t_ifassert_timer,
623 on_assert_timer,
624 ch, interval);
625}
626
627static void pim_assert_timer_reset(struct pim_ifchannel *ch)
628{
629 pim_assert_timer_set(ch, PIM_ASSERT_TIME - PIM_ASSERT_OVERRIDE_INTERVAL);
630}
631
632/*
633 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
634
635 (S,G) Assert State machine Actions
636
637 A1: Send Assert(S,G).
638 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
639 Store self as AssertWinner(S,G,I).
640 Store spt_assert_metric(S,I) as AssertWinnerMetric(S,G,I).
641*/
642int assert_action_a1(struct pim_ifchannel *ch)
643{
644 struct interface *ifp = ch->interface;
645 struct pim_interface *pim_ifp;
646
647 zassert(ifp);
648
649 pim_ifp = ifp->info;
650 if (!pim_ifp) {
651 char src_str[100];
652 char grp_str[100];
653 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
654 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
655 zlog_warn("%s: (S,G)=(%s,%s) multicast no enabled on interface %s",
656 __PRETTY_FUNCTION__,
657 src_str, grp_str, ifp->name);
658 return -1; /* must return since pim_ifp is used below */
659 }
660
661 /* Switch to I_AM_WINNER before performing action_a3 below */
662 pim_ifassert_winner_set(ch, PIM_IFASSERT_I_AM_WINNER,
663 pim_ifp->primary_address,
664 pim_macro_spt_assert_metric(&ch->upstream->rpf,
665 pim_ifp->primary_address));
666
667 zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
668 if (assert_action_a3(ch)) {
669 char src_str[100];
670 char grp_str[100];
671 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
672 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
673 zlog_warn("%s: (S,G)=(%s,%s) assert_action_a3 failure on interface %s",
674 __PRETTY_FUNCTION__,
675 src_str, grp_str, ifp->name);
676 /* warning only */
677 }
678
679 zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
680
681 return 0;
682}
683
684/*
685 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
686
687 (S,G) Assert State machine Actions
688
689 A2: Store new assert winner as AssertWinner(S,G,I) and assert
690 winner metric as AssertWinnerMetric(S,G,I).
691 Set Assert Timer to Assert_Time.
692*/
693static void assert_action_a2(struct pim_ifchannel *ch,
694 struct pim_assert_metric winner_metric)
695{
696 pim_ifassert_winner_set(ch, PIM_IFASSERT_I_AM_LOSER,
697 winner_metric.ip_address,
698 winner_metric);
699
700 pim_assert_timer_set(ch, PIM_ASSERT_TIME);
701
702 zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER);
703}
704
705/*
706 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
707
708 (S,G) Assert State machine Actions
709
710 A3: Send Assert(S,G).
711 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
712*/
713static int assert_action_a3(struct pim_ifchannel *ch)
714{
715 zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
716
717 pim_assert_timer_reset(ch);
718
719 if (pim_assert_send(ch)) {
720 char src_str[100];
721 char grp_str[100];
722 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
723 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
724
725 zlog_warn("%s: (S,G)=(%s,%s) failure sending assert on interface %s",
726 __PRETTY_FUNCTION__,
727 src_str, grp_str, ch->interface->name);
728 return -1;
729 }
730
731 zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
732
733 return 0;
734}
735
736/*
737 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
738
739 (S,G) Assert State machine Actions
740
741 A4: Send AssertCancel(S,G).
742 Delete assert info (AssertWinner(S,G,I) and
743 AssertWinnerMetric(S,G,I) will then return their default
744 values).
745*/
746void assert_action_a4(struct pim_ifchannel *ch)
747{
748 if (pim_assert_cancel(ch)) {
749 char src_str[100];
750 char grp_str[100];
751 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
752 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
753 zlog_warn("%s: failure sending AssertCancel(%s,%s) on interface %s",
754 __PRETTY_FUNCTION__,
755 src_str, grp_str, ch->interface->name);
756 /* log warning only */
757 }
758
759 assert_action_a5(ch);
760
761 zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO);
762}
763
764/*
765 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
766
767 (S,G) Assert State machine Actions
768
769 A5: Delete assert info (AssertWinner(S,G,I) and
770 AssertWinnerMetric(S,G,I) will then return their default values).
771*/
772void assert_action_a5(struct pim_ifchannel *ch)
773{
774 reset_ifassert_state(ch);
775 zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO);
776}
777
778/*
779 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
780
781 (S,G) Assert State machine Actions
782
783 A6: Store new assert winner as AssertWinner(S,G,I) and assert
784 winner metric as AssertWinnerMetric(S,G,I).
785 Set Assert Timer to Assert_Time.
786 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true)
787 set SPTbit(S,G) to TRUE.
788*/
789static void assert_action_a6(struct pim_ifchannel *ch,
790 struct pim_assert_metric winner_metric)
791{
792 assert_action_a2(ch, winner_metric);
793
794 /*
795 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) set
796 SPTbit(S,G) to TRUE.
797
798 Notice: For PIM SSM, SPTbit(S,G) is already always true.
799 */
800
801 zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER);
802}
803