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