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