Coverage Report

Created: 2025-11-11 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/pimd/pim_tib.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * TIB (Tree Information Base) - just PIM <> IGMP/MLD glue for now
4
 * Copyright (C) 2022  David Lamparter for NetDEF, Inc.
5
 */
6
7
#include <zebra.h>
8
9
#include "pim_tib.h"
10
11
#include "pimd.h"
12
#include "pim_instance.h"
13
#include "pim_iface.h"
14
#include "pim_upstream.h"
15
#include "pim_oil.h"
16
#include "pim_nht.h"
17
18
static struct channel_oil *
19
tib_sg_oil_setup(struct pim_instance *pim, pim_sgaddr sg, struct interface *oif)
20
0
{
21
0
  struct pim_interface *pim_oif = oif->info;
22
0
  int input_iface_vif_index = 0;
23
0
  pim_addr vif_source;
24
0
  struct prefix grp;
25
0
  struct pim_nexthop nexthop;
26
0
  struct pim_upstream *up = NULL;
27
28
0
  if (!pim_rp_set_upstream_addr(pim, &vif_source, sg.src, sg.grp)) {
29
    /* no PIM RP - create a dummy channel oil */
30
0
    return pim_channel_oil_add(pim, &sg, __func__);
31
0
  }
32
33
0
  pim_addr_to_prefix(&grp, sg.grp);
34
35
0
  up = pim_upstream_find(pim, &sg);
36
0
  if (up) {
37
0
    memcpy(&nexthop, &up->rpf.source_nexthop,
38
0
           sizeof(struct pim_nexthop));
39
0
    (void)pim_ecmp_nexthop_lookup(pim, &nexthop, vif_source, &grp,
40
0
                0);
41
0
    if (nexthop.interface)
42
0
      input_iface_vif_index = pim_if_find_vifindex_by_ifindex(
43
0
        pim, nexthop.interface->ifindex);
44
0
  } else
45
0
    input_iface_vif_index =
46
0
      pim_ecmp_fib_lookup_if_vif_index(pim, vif_source, &grp);
47
48
0
  if (PIM_DEBUG_ZEBRA)
49
0
    zlog_debug("%s: NHT %pSG vif_source %pPAs vif_index:%d",
50
0
         __func__, &sg, &vif_source, input_iface_vif_index);
51
52
0
  if (input_iface_vif_index < 1) {
53
0
    if (PIM_DEBUG_GM_TRACE)
54
0
      zlog_debug(
55
0
        "%s %s: could not find input interface for %pSG",
56
0
        __FILE__, __func__, &sg);
57
58
0
    return pim_channel_oil_add(pim, &sg, __func__);
59
0
  }
60
61
  /*
62
   * Protect IGMP against adding looped MFC entries created by both
63
   * source and receiver attached to the same interface. See TODO T22.
64
   * Block only when the intf is non DR DR must create upstream.
65
   */
66
0
  if ((input_iface_vif_index == pim_oif->mroute_vif_index) &&
67
0
      !(PIM_I_am_DR(pim_oif))) {
68
    /* ignore request for looped MFC entry */
69
0
    if (PIM_DEBUG_GM_TRACE)
70
0
      zlog_debug(
71
0
        "%s: ignoring request for looped MFC entry (S,G)=%pSG: oif=%s vif_index=%d",
72
0
        __func__, &sg, oif->name,
73
0
        input_iface_vif_index);
74
75
0
    return NULL;
76
0
  }
77
78
0
  return pim_channel_oil_add(pim, &sg, __func__);
79
0
}
80
81
bool tib_sg_gm_join(struct pim_instance *pim, pim_sgaddr sg,
82
        struct interface *oif, struct channel_oil **oilp)
83
0
{
84
0
  struct pim_interface *pim_oif = oif->info;
85
86
0
  if (!pim_oif) {
87
0
    if (PIM_DEBUG_GM_TRACE)
88
0
      zlog_debug("%s: multicast not enabled on oif=%s?",
89
0
           __func__, oif->name);
90
0
    return false;
91
0
  }
92
93
0
  if (!*oilp)
94
0
    *oilp = tib_sg_oil_setup(pim, sg, oif);
95
0
  if (!*oilp)
96
0
    return false;
97
98
0
  if (PIM_I_am_DR(pim_oif) || PIM_I_am_DualActive(pim_oif)) {
99
0
    int result;
100
101
0
    result = pim_channel_add_oif(*oilp, oif, PIM_OIF_FLAG_PROTO_GM,
102
0
               __func__);
103
0
    if (result) {
104
0
      if (PIM_DEBUG_MROUTE)
105
0
        zlog_warn("%s: add_oif() failed with return=%d",
106
0
            __func__, result);
107
0
      return false;
108
0
    }
109
0
  } else {
110
0
    if (PIM_DEBUG_GM_TRACE)
111
0
      zlog_debug(
112
0
        "%s: %pSG was received on %s interface but we are not DR for that interface",
113
0
        __func__, &sg, oif->name);
114
115
0
    return false;
116
0
  }
117
  /*
118
    Feed IGMPv3-gathered local membership information into PIM
119
    per-interface (S,G) state.
120
   */
121
0
  if (!pim_ifchannel_local_membership_add(oif, &sg, false /*is_vxlan*/)) {
122
0
    if (PIM_DEBUG_MROUTE)
123
0
      zlog_warn(
124
0
        "%s: Failure to add local membership for %pSG",
125
0
        __func__, &sg);
126
127
0
    pim_channel_del_oif(*oilp, oif, PIM_OIF_FLAG_PROTO_GM,
128
0
            __func__);
129
0
    return false;
130
0
  }
131
132
0
  return true;
133
0
}
134
135
void tib_sg_gm_prune(struct pim_instance *pim, pim_sgaddr sg,
136
         struct interface *oif, struct channel_oil **oilp)
137
0
{
138
0
  int result;
139
140
  /*
141
   It appears that in certain circumstances that
142
   igmp_source_forward_stop is called when IGMP forwarding
143
   was not enabled in oif_flags for this outgoing interface.
144
   Possibly because of multiple calls. When that happens, we
145
   enter the below if statement and this function returns early
146
   which in turn triggers the calling function to assert.
147
   Making the call to pim_channel_del_oif and ignoring the return code
148
   fixes the issue without ill effect, similar to
149
   pim_forward_stop below.
150
  */
151
0
  result = pim_channel_del_oif(*oilp, oif, PIM_OIF_FLAG_PROTO_GM,
152
0
             __func__);
153
0
  if (result) {
154
0
    if (PIM_DEBUG_GM_TRACE)
155
0
      zlog_debug(
156
0
        "%s: pim_channel_del_oif() failed with return=%d",
157
0
        __func__, result);
158
0
    return;
159
0
  }
160
161
  /*
162
    Feed IGMPv3-gathered local membership information into PIM
163
    per-interface (S,G) state.
164
   */
165
0
  pim_ifchannel_local_membership_del(oif, &sg);
166
0
}