Coverage Report

Created: 2025-08-03 06:36

/src/frr/pimd/pim_oil.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 "memory.h"
11
#include "linklist.h"
12
#include "if.h"
13
#include "hash.h"
14
#include "jhash.h"
15
16
#include "pimd.h"
17
#include "pim_oil.h"
18
#include "pim_str.h"
19
#include "pim_iface.h"
20
#include "pim_time.h"
21
#include "pim_vxlan.h"
22
23
static void pim_channel_update_mute(struct channel_oil *c_oil);
24
25
char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size)
26
0
{
27
0
  char *out;
28
0
  struct interface *ifp;
29
0
  pim_sgaddr sg;
30
0
  int i;
31
32
0
  sg.src = *oil_origin(c_oil);
33
0
  sg.grp = *oil_mcastgrp(c_oil);
34
0
  ifp = pim_if_find_by_vif_index(c_oil->pim, *oil_parent(c_oil));
35
0
  snprintfrr(buf, size, "%pSG IIF: %s, OIFS: ", &sg,
36
0
       ifp ? ifp->name : "(?)");
37
38
0
  out = buf + strlen(buf);
39
0
  for (i = 0; i < MAXVIFS; i++) {
40
0
    if (oil_if_has(c_oil, i) != 0) {
41
0
      ifp = pim_if_find_by_vif_index(c_oil->pim, i);
42
0
      snprintf(out, buf + size - out, "%s ",
43
0
         ifp ? ifp->name : "(?)");
44
0
      out += strlen(out);
45
0
    }
46
0
  }
47
48
0
  return buf;
49
0
}
50
51
int pim_channel_oil_compare(const struct channel_oil *cc1,
52
          const struct channel_oil *cc2)
53
935k
{
54
935k
  struct channel_oil *c1 = (struct channel_oil *)cc1;
55
935k
  struct channel_oil *c2 = (struct channel_oil *)cc2;
56
935k
  int rv;
57
58
935k
  rv = pim_addr_cmp(*oil_mcastgrp(c1), *oil_mcastgrp(c2));
59
935k
  if (rv)
60
645k
    return rv;
61
290k
  rv = pim_addr_cmp(*oil_origin(c1), *oil_origin(c2));
62
290k
  if (rv)
63
290k
    return rv;
64
0
  return 0;
65
290k
}
66
67
void pim_oil_init(struct pim_instance *pim)
68
1
{
69
1
  rb_pim_oil_init(&pim->channel_oil_head);
70
1
}
71
72
void pim_oil_terminate(struct pim_instance *pim)
73
0
{
74
0
  struct channel_oil *c_oil;
75
76
0
  while ((c_oil = rb_pim_oil_pop(&pim->channel_oil_head)))
77
0
    pim_channel_oil_free(c_oil);
78
79
0
  rb_pim_oil_fini(&pim->channel_oil_head);
80
0
}
81
82
void pim_channel_oil_free(struct channel_oil *c_oil)
83
46.0k
{
84
46.0k
  XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
85
46.0k
}
86
87
struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
88
           pim_sgaddr *sg)
89
46.9k
{
90
46.9k
  struct channel_oil *c_oil = NULL;
91
46.9k
  struct channel_oil lookup;
92
93
46.9k
  *oil_mcastgrp(&lookup) = sg->grp;
94
46.9k
  *oil_origin(&lookup) = sg->src;
95
96
46.9k
  c_oil = rb_pim_oil_find(&pim->channel_oil_head, &lookup);
97
98
46.9k
  return c_oil;
99
46.9k
}
100
101
struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
102
          pim_sgaddr *sg, const char *name)
103
46.9k
{
104
46.9k
  struct channel_oil *c_oil;
105
106
46.9k
  c_oil = pim_find_channel_oil(pim, sg);
107
46.9k
  if (c_oil) {
108
0
    ++c_oil->oil_ref_count;
109
110
0
    if (!c_oil->up) {
111
      /* channel might be present prior to upstream */
112
0
      c_oil->up = pim_upstream_find(
113
0
          pim, sg);
114
      /* if the upstream entry is being anchored to an
115
       * already existing channel OIL we need to re-evaluate
116
       * the "Mute" state on AA OIFs
117
       */
118
0
      pim_channel_update_mute(c_oil);
119
0
    }
120
121
    /* check if the IIF has changed
122
     * XXX - is this really needed
123
     */
124
0
    pim_upstream_mroute_iif_update(c_oil, __func__);
125
126
0
    if (PIM_DEBUG_MROUTE)
127
0
      zlog_debug(
128
0
        "%s(%s): Existing oil for %pSG Ref Count: %d (Post Increment)",
129
0
        __func__, name, sg, c_oil->oil_ref_count);
130
0
    return c_oil;
131
0
  }
132
133
46.9k
  c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
134
135
46.9k
  *oil_mcastgrp(c_oil) = sg->grp;
136
46.9k
  *oil_origin(c_oil) = sg->src;
137
138
46.9k
  *oil_parent(c_oil) = MAXVIFS;
139
46.9k
  c_oil->oil_ref_count = 1;
140
46.9k
  c_oil->installed = 0;
141
46.9k
  c_oil->up = pim_upstream_find(pim, sg);
142
46.9k
  c_oil->pim = pim;
143
144
46.9k
  rb_pim_oil_add(&pim->channel_oil_head, c_oil);
145
146
46.9k
  if (PIM_DEBUG_MROUTE)
147
0
    zlog_debug("%s(%s): c_oil %pSG add", __func__, name, sg);
148
149
46.9k
  return c_oil;
150
46.9k
}
151
152
153
/*
154
 * Clean up mroute and channel oil created for dropping pkts from directly
155
 * connected source when the interface was non DR.
156
 */
157
void pim_clear_nocache_state(struct pim_interface *pim_ifp)
158
0
{
159
0
  struct channel_oil *c_oil;
160
161
0
  frr_each_safe (rb_pim_oil, &pim_ifp->pim->channel_oil_head, c_oil) {
162
163
0
    if ((!c_oil->up) ||
164
0
        !(PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(c_oil->up->flags)))
165
0
      continue;
166
167
0
    if (*oil_parent(c_oil) != pim_ifp->mroute_vif_index)
168
0
      continue;
169
170
0
    EVENT_OFF(c_oil->up->t_ka_timer);
171
0
    PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(c_oil->up->flags);
172
0
    PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(c_oil->up->flags);
173
0
    pim_upstream_del(pim_ifp->pim, c_oil->up, __func__);
174
0
  }
175
0
}
176
177
struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil,
178
          const char *name)
179
46.0k
{
180
46.0k
  if (PIM_DEBUG_MROUTE) {
181
0
    pim_sgaddr sg = {.src = *oil_origin(c_oil),
182
0
         .grp = *oil_mcastgrp(c_oil)};
183
184
0
    zlog_debug(
185
0
      "%s(%s): Del oil for %pSG, Ref Count: %d (Predecrement)",
186
0
      __func__, name, &sg, c_oil->oil_ref_count);
187
0
  }
188
46.0k
  --c_oil->oil_ref_count;
189
190
46.0k
  if (c_oil->oil_ref_count < 1) {
191
    /*
192
     * notice that listnode_delete() can't be moved
193
     * into pim_channel_oil_free() because the later is
194
     * called by list_delete_all_node()
195
     */
196
46.0k
    c_oil->up = NULL;
197
46.0k
    rb_pim_oil_del(&c_oil->pim->channel_oil_head, c_oil);
198
199
46.0k
    pim_channel_oil_free(c_oil);
200
46.0k
    return NULL;
201
46.0k
  }
202
203
0
  return c_oil;
204
46.0k
}
205
206
void pim_channel_oil_upstream_deref(struct channel_oil *c_oil)
207
46.0k
{
208
  /* The upstream entry associated with a channel_oil is abt to be
209
   * deleted. If the channel_oil is kept around because of other
210
   * references we need to remove upstream based states out of it.
211
   */
212
46.0k
  c_oil = pim_channel_oil_del(c_oil, __func__);
213
46.0k
  if (c_oil) {
214
    /* note: here we assume that c_oil->up has already been
215
     * cleared
216
     */
217
0
    pim_channel_update_mute(c_oil);
218
0
  }
219
46.0k
}
220
221
int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
222
      uint32_t proto_mask, const char *caller)
223
51.8k
{
224
51.8k
  struct pim_interface *pim_ifp;
225
226
51.8k
  assert(channel_oil);
227
51.8k
  assert(oif);
228
229
51.8k
  pim_ifp = oif->info;
230
231
51.8k
  assertf(pim_ifp->mroute_vif_index >= 0,
232
51.8k
    "trying to del OIF %s with VIF (%d)", oif->name,
233
51.8k
    pim_ifp->mroute_vif_index);
234
235
  /*
236
   * Don't do anything if we've been asked to remove a source
237
   * that is not actually on it.
238
   */
239
51.8k
  if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) {
240
51.4k
    if (PIM_DEBUG_MROUTE) {
241
0
      zlog_debug(
242
0
        "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)",
243
0
        __FILE__, __func__, proto_mask,
244
0
        channel_oil
245
0
          ->oif_flags[pim_ifp->mroute_vif_index],
246
0
        oif->name, pim_ifp->mroute_vif_index,
247
0
        oil_if_has(channel_oil, pim_ifp->mroute_vif_index),
248
0
        oil_origin(channel_oil),
249
0
        oil_mcastgrp(channel_oil));
250
0
    }
251
51.4k
    return 0;
252
51.4k
  }
253
254
383
  channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
255
256
383
  if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] &
257
383
      PIM_OIF_FLAG_PROTO_ANY) {
258
204
    if (PIM_DEBUG_MROUTE) {
259
0
      zlog_debug(
260
0
        "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)",
261
0
        __FILE__, __func__, oif->name,
262
0
        pim_ifp->mroute_vif_index,
263
0
        oil_if_has(channel_oil, pim_ifp->mroute_vif_index),
264
0
        oil_origin(channel_oil),
265
0
        oil_mcastgrp(channel_oil));
266
0
    }
267
204
    return 0;
268
204
  }
269
270
179
  oil_if_set(channel_oil, pim_ifp->mroute_vif_index, 0);
271
  /* clear mute; will be re-evaluated when the OIF becomes valid again */
272
179
  channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~PIM_OIF_FLAG_MUTE;
273
274
179
  if (pim_upstream_mroute_add(channel_oil, __func__)) {
275
0
    if (PIM_DEBUG_MROUTE) {
276
0
      zlog_debug(
277
0
        "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%pPAs,%pPAs)",
278
0
        __FILE__, __func__, oif->name,
279
0
        pim_ifp->mroute_vif_index,
280
0
        oil_origin(channel_oil),
281
0
        oil_mcastgrp(channel_oil));
282
0
    }
283
0
    return -1;
284
0
  }
285
286
179
  --channel_oil->oil_size;
287
288
179
  if (PIM_DEBUG_MROUTE) {
289
0
    zlog_debug(
290
0
      "%s(%s): (S,G)=(%pPAs,%pPAs): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
291
0
      __func__, caller, oil_origin(channel_oil),
292
0
      oil_mcastgrp(channel_oil),
293
0
      proto_mask,
294
0
      *oil_parent(channel_oil), oif->name,
295
0
      pim_ifp->mroute_vif_index);
296
0
  }
297
298
179
  return 0;
299
179
}
300
301
void pim_channel_del_inherited_oif(struct channel_oil *c_oil,
302
    struct interface *oif, const char *caller)
303
5.73k
{
304
5.73k
  struct pim_upstream *up = c_oil->up;
305
306
5.73k
  pim_channel_del_oif(c_oil, oif, PIM_OIF_FLAG_PROTO_STAR,
307
5.73k
      caller);
308
309
  /* if an inherited OIF is being removed join-desired can change
310
   * if the inherited OIL is now empty and KAT is running
311
   */
312
5.73k
  if (up && !pim_addr_is_any(up->sg.src) &&
313
5.73k
      pim_upstream_empty_inherited_olist(up))
314
5.28k
    pim_upstream_update_join_desired(up->pim, up);
315
5.73k
}
316
317
static bool pim_channel_eval_oif_mute(struct channel_oil *c_oil,
318
    struct pim_interface *pim_ifp)
319
46.3k
{
320
46.3k
  struct pim_interface *pim_reg_ifp;
321
46.3k
  struct pim_interface *vxlan_ifp;
322
46.3k
  bool do_mute = false;
323
46.3k
  struct pim_instance *pim = c_oil->pim;
324
325
46.3k
  if (!c_oil->up)
326
0
    return do_mute;
327
328
46.3k
  pim_reg_ifp = pim->regiface->info;
329
46.3k
  if (pim_ifp == pim_reg_ifp) {
330
    /* suppress pimreg in the OIL if the mroute is not supposed to
331
     * trigger register encapsulated data
332
     */
333
0
    if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil->up->flags))
334
0
      do_mute = true;
335
336
0
    return do_mute;
337
0
  }
338
339
46.3k
  vxlan_ifp = pim_vxlan_get_term_ifp(pim);
340
46.3k
  if (pim_ifp == vxlan_ifp) {
341
    /* 1. vxlan termination device must never be added to the
342
     * origination mroute (and that can actually happen because
343
     * of XG inheritance from the termination mroute) otherwise
344
     * traffic will end up looping.
345
     * PS: This check has also been extended to non-orig mroutes
346
     * that have a local SIP as such mroutes can move back and
347
     * forth between orig<=>non-orig type.
348
     * 2. vxlan termination device should be removed from the non-DF
349
     * to prevent duplicates to the overlay rxer
350
     */
351
0
    if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil->up->flags) ||
352
0
      PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags) ||
353
0
      pim_vxlan_is_local_sip(c_oil->up))
354
0
      do_mute = true;
355
356
0
    return do_mute;
357
0
  }
358
359
46.3k
  if (PIM_I_am_DualActive(pim_ifp)) {
360
0
    struct pim_upstream *starup = c_oil->up->parent;
361
0
    if (PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(c_oil->up->flags)
362
0
        && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags)))
363
0
      do_mute = true;
364
365
    /* In case entry is (S,G), Negotiation happens at (*.G) */
366
0
    if (starup
367
368
0
        && PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(starup->flags)
369
0
        && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(starup->flags)))
370
0
      do_mute = true;
371
0
    return do_mute;
372
0
  }
373
46.3k
  return do_mute;
374
46.3k
}
375
376
void pim_channel_update_oif_mute(struct channel_oil *c_oil,
377
    struct pim_interface *pim_ifp)
378
0
{
379
0
  bool old_mute;
380
0
  bool new_mute;
381
382
  /* If pim_ifp is not a part of the OIL there is nothing to do */
383
0
  if (!oil_if_has(c_oil, pim_ifp->mroute_vif_index))
384
0
    return;
385
386
0
  old_mute = !!(c_oil->oif_flags[pim_ifp->mroute_vif_index] &
387
0
      PIM_OIF_FLAG_MUTE);
388
0
  new_mute = pim_channel_eval_oif_mute(c_oil, pim_ifp);
389
0
  if (old_mute == new_mute)
390
0
    return;
391
392
0
  if (new_mute)
393
0
    c_oil->oif_flags[pim_ifp->mroute_vif_index] |=
394
0
      PIM_OIF_FLAG_MUTE;
395
0
  else
396
0
    c_oil->oif_flags[pim_ifp->mroute_vif_index] &=
397
0
      ~PIM_OIF_FLAG_MUTE;
398
399
0
  pim_upstream_mroute_add(c_oil, __func__);
400
0
}
401
402
/* pim_upstream has been set or cleared on the c_oil. re-eval mute state
403
 * on all existing OIFs
404
 */
405
static void pim_channel_update_mute(struct channel_oil *c_oil)
406
0
{
407
0
  struct pim_interface *pim_reg_ifp;
408
0
  struct pim_interface *vxlan_ifp;
409
410
0
  if (c_oil->pim->regiface) {
411
0
    pim_reg_ifp = c_oil->pim->regiface->info;
412
0
    if (pim_reg_ifp)
413
0
      pim_channel_update_oif_mute(c_oil, pim_reg_ifp);
414
0
  }
415
0
  vxlan_ifp = pim_vxlan_get_term_ifp(c_oil->pim);
416
0
  if (vxlan_ifp)
417
0
    pim_channel_update_oif_mute(c_oil, vxlan_ifp);
418
0
}
419
420
int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
421
      uint32_t proto_mask, const char *caller)
422
49.7k
{
423
49.7k
  struct pim_interface *pim_ifp;
424
49.7k
  int old_ttl;
425
426
  /*
427
   * If we've gotten here we've gone bad, but let's
428
   * not take down pim
429
   */
430
49.7k
  if (!channel_oil) {
431
0
    zlog_warn("Attempt to Add OIF for non-existent channel oil");
432
0
    return -1;
433
0
  }
434
435
49.7k
  pim_ifp = oif->info;
436
437
49.7k
  assertf(pim_ifp->mroute_vif_index >= 0,
438
49.7k
    "trying to add OIF %s with VIF (%d)", oif->name,
439
49.7k
    pim_ifp->mroute_vif_index);
440
441
  /* Prevent single protocol from subscribing same interface to
442
     channel (S,G) multiple times */
443
49.7k
  if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
444
3.17k
    channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
445
446
3.17k
    if (PIM_DEBUG_MROUTE) {
447
0
      zlog_debug(
448
0
        "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)",
449
0
        __FILE__, __func__, proto_mask, oif->name,
450
0
        pim_ifp->mroute_vif_index,
451
0
        oil_if_has(channel_oil, pim_ifp->mroute_vif_index),
452
0
        oil_origin(channel_oil),
453
0
        oil_mcastgrp(channel_oil));
454
0
    }
455
3.17k
    return -3;
456
3.17k
  }
457
458
  /* Allow other protocol to request subscription of same interface to
459
   * channel (S,G), we need to note this information
460
   */
461
46.5k
  if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]
462
46.5k
      & PIM_OIF_FLAG_PROTO_ANY) {
463
464
    /* Updating time here is not required as this time has to
465
     * indicate when the interface is added
466
     */
467
468
206
    channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
469
    /* Check the OIF really exists before returning, and only log
470
       warning otherwise */
471
206
    if (oil_if_has(channel_oil, pim_ifp->mroute_vif_index) < 1) {
472
0
      zlog_warn(
473
0
        "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)",
474
0
        __FILE__, __func__, proto_mask, oif->name,
475
0
        pim_ifp->mroute_vif_index,
476
0
        oil_if_has(channel_oil, pim_ifp->mroute_vif_index),
477
0
        oil_origin(channel_oil),
478
0
        oil_mcastgrp(channel_oil));
479
0
    }
480
481
206
    if (PIM_DEBUG_MROUTE) {
482
0
      zlog_debug(
483
0
        "%s(%s): (S,G)=(%pPAs,%pPAs): proto_mask=%u OIF=%s vif_index=%d added to 0x%x",
484
0
        __func__, caller, oil_origin(channel_oil),
485
0
        oil_mcastgrp(channel_oil),
486
0
        proto_mask, oif->name,
487
0
        pim_ifp->mroute_vif_index,
488
0
        channel_oil
489
0
          ->oif_flags[pim_ifp->mroute_vif_index]);
490
0
    }
491
206
    return 0;
492
206
  }
493
494
46.3k
  old_ttl = oil_if_has(channel_oil, pim_ifp->mroute_vif_index);
495
496
46.3k
  if (old_ttl > 0) {
497
0
    if (PIM_DEBUG_MROUTE) {
498
0
      zlog_debug(
499
0
        "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%pPAs,%pPAs)",
500
0
        __FILE__, __func__, oif->name,
501
0
        pim_ifp->mroute_vif_index,
502
0
        oil_origin(channel_oil),
503
0
        oil_mcastgrp(channel_oil));
504
0
    }
505
0
    return -4;
506
0
  }
507
508
46.3k
  oil_if_set(channel_oil, pim_ifp->mroute_vif_index, PIM_MROUTE_MIN_TTL);
509
510
  /* Some OIFs are held in a muted state i.e. the PIM state machine
511
   * decided to include the OIF but additional status check such as
512
   * MLAG DF role prevent it from being activated for traffic
513
   * forwarding.
514
   */
515
46.3k
  if (pim_channel_eval_oif_mute(channel_oil, pim_ifp))
516
0
    channel_oil->oif_flags[pim_ifp->mroute_vif_index] |=
517
0
      PIM_OIF_FLAG_MUTE;
518
46.3k
  else
519
46.3k
    channel_oil->oif_flags[pim_ifp->mroute_vif_index] &=
520
46.3k
      ~PIM_OIF_FLAG_MUTE;
521
522
  /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not
523
   * valid to get installed in kernel.
524
   */
525
46.3k
  if (*oil_parent(channel_oil) != MAXVIFS) {
526
0
    if (pim_upstream_mroute_add(channel_oil, __func__)) {
527
0
      if (PIM_DEBUG_MROUTE) {
528
0
        zlog_debug(
529
0
          "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%pPAs,%pPAs)",
530
0
          __FILE__, __func__, oif->name,
531
0
          pim_ifp->mroute_vif_index,
532
0
          oil_origin(channel_oil),
533
0
          oil_mcastgrp(channel_oil));
534
0
      }
535
536
0
      oil_if_set(channel_oil, pim_ifp->mroute_vif_index,
537
0
           old_ttl);
538
0
      return -5;
539
0
    }
540
0
  }
541
542
46.3k
  channel_oil->oif_creation[pim_ifp->mroute_vif_index] =
543
46.3k
    pim_time_monotonic_sec();
544
46.3k
  ++channel_oil->oil_size;
545
46.3k
  channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
546
547
46.3k
  if (PIM_DEBUG_MROUTE) {
548
0
    zlog_debug(
549
0
      "%s(%s): (S,G)=(%pPAs,%pPAs): proto_mask=%u OIF=%s vif_index=%d: DONE",
550
0
      __func__, caller, oil_origin(channel_oil),
551
0
      oil_mcastgrp(channel_oil),
552
0
      proto_mask,
553
0
      oif->name, pim_ifp->mroute_vif_index);
554
0
  }
555
556
46.3k
  return 0;
557
46.3k
}
558
559
int pim_channel_oil_empty(struct channel_oil *c_oil)
560
57.1k
{
561
57.1k
  static struct channel_oil null_oil;
562
563
57.1k
  if (!c_oil)
564
0
    return 1;
565
566
  /* exclude pimreg from the OIL when checking if the inherited_oil is
567
   * non-NULL.
568
   * pimreg device (in all vrfs) uses a vifi of
569
   * 0 (PIM_OIF_PIM_REGISTER_VIF) so we simply mfcc_ttls[0] */
570
57.1k
  if (oil_if_has(c_oil, 0))
571
0
    oil_if_set(&null_oil, 0, 1);
572
57.1k
  else
573
57.1k
    oil_if_set(&null_oil, 0, 0);
574
575
57.1k
  return !oil_if_cmp(&c_oil->oil, &null_oil.oil);
576
57.1k
}