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