Coverage Report

Created: 2025-07-14 06:48

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