]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_oil.c
pimd: Refactor s,g information to struct prefix in pim_upstream.h
[mirror_frr.git] / pimd / pim_oil.c
1 /*
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 "memory.h"
27 #include "linklist.h"
28 #include "if.h"
29
30 #include "pimd.h"
31 #include "pim_oil.h"
32 #include "pim_str.h"
33 #include "pim_iface.h"
34 #include "pim_time.h"
35
36 void pim_channel_oil_free(struct channel_oil *c_oil)
37 {
38 XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
39 }
40
41 static void pim_channel_oil_delete(struct channel_oil *c_oil)
42 {
43 /*
44 notice that listnode_delete() can't be moved
45 into pim_channel_oil_free() because the later is
46 called by list_delete_all_node()
47 */
48 listnode_delete(qpim_channel_oil_list, c_oil);
49
50 pim_channel_oil_free(c_oil);
51 }
52
53 static struct channel_oil *channel_oil_new(struct prefix *sg,
54 int input_vif_index)
55 {
56 struct channel_oil *c_oil;
57 struct interface *ifp_in;
58
59 ifp_in = pim_if_find_by_vif_index(input_vif_index);
60 if (!ifp_in) {
61 /* warning only */
62 zlog_warn("%s: (S,G)=%s could not find input interface for input_vif_index=%d",
63 __PRETTY_FUNCTION__,
64 pim_str_sg_dump (sg), input_vif_index);
65 }
66
67 c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
68 if (!c_oil) {
69 zlog_err("PIM XCALLOC(%zu) failure", sizeof(*c_oil));
70 return 0;
71 }
72
73 c_oil->oil.mfcc_mcastgrp = sg->u.sg.grp;
74 c_oil->oil.mfcc_origin = sg->u.sg.src;
75 c_oil->oil.mfcc_parent = input_vif_index;
76 c_oil->oil_ref_count = 1;
77 c_oil->installed = 0;
78
79 zassert(c_oil->oil_size == 0);
80
81 return c_oil;
82 }
83
84 static struct channel_oil *pim_add_channel_oil(struct prefix *sg,
85 int input_vif_index)
86 {
87 struct channel_oil *c_oil;
88
89 c_oil = channel_oil_new(sg, input_vif_index);
90 if (!c_oil) {
91 zlog_warn("PIM XCALLOC(%zu) failure", sizeof(*c_oil));
92 return 0;
93 }
94
95 listnode_add(qpim_channel_oil_list, c_oil);
96
97 return c_oil;
98 }
99
100 static struct channel_oil *pim_find_channel_oil(struct prefix *sg)
101 {
102 struct listnode *node;
103 struct channel_oil *c_oil;
104
105 for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
106 if ((sg->u.sg.grp.s_addr == c_oil->oil.mfcc_mcastgrp.s_addr) &&
107 (sg->u.sg.src.s_addr == c_oil->oil.mfcc_origin.s_addr))
108 return c_oil;
109 }
110
111 return 0;
112 }
113
114 struct channel_oil *pim_channel_oil_add(struct prefix *sg,
115 int input_vif_index)
116 {
117 struct channel_oil *c_oil;
118
119 c_oil = pim_find_channel_oil(sg);
120 if (c_oil) {
121 ++c_oil->oil_ref_count;
122 return c_oil;
123 }
124
125 return pim_add_channel_oil(sg, input_vif_index);
126 }
127
128 void pim_channel_oil_del(struct channel_oil *c_oil)
129 {
130 --c_oil->oil_ref_count;
131
132 if (c_oil->oil_ref_count < 1) {
133 pim_channel_oil_delete(c_oil);
134 }
135 }
136
137 int
138 pim_channel_del_oif (struct channel_oil *channel_oil,
139 struct interface *oif,
140 uint32_t proto_mask)
141 {
142 struct pim_interface *pim_ifp;
143
144 zassert (channel_oil);
145 zassert (oif);
146
147 pim_ifp = oif->info;
148
149 if (PIM_DEBUG_MROUTE)
150 {
151 char group_str[100];
152 char source_str[100];
153 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
154 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
155 zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d",
156 __FILE__, __PRETTY_FUNCTION__,
157 source_str, group_str,
158 proto_mask, oif->name, pim_ifp->mroute_vif_index);
159 }
160
161 /*
162 * Don't do anything if we've been asked to remove a source
163 * that is not actually on it.
164 */
165 if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask))
166 {
167 if (PIM_DEBUG_MROUTE)
168 {
169 char group_str[100];
170 char source_str[100];
171 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
172 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
173 zlog_debug("%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
174 __FILE__, __PRETTY_FUNCTION__,
175 proto_mask, channel_oil->oif_flags[pim_ifp->mroute_vif_index],
176 oif->name, pim_ifp->mroute_vif_index,
177 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
178 source_str, group_str);
179 }
180 return 0;
181 }
182
183 channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
184
185 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index])
186 {
187 if (PIM_DEBUG_MROUTE)
188 {
189 char group_str[100];
190 char source_str[100];
191 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
192 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
193 zlog_debug("%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
194 __FILE__, __PRETTY_FUNCTION__,
195 oif->name, pim_ifp->mroute_vif_index,
196 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
197 source_str, group_str);
198 }
199 return 0;
200 }
201
202 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0;
203
204 if (pim_mroute_add (channel_oil)) {
205 if (PIM_DEBUG_MROUTE)
206 {
207 char group_str[100];
208 char source_str[100];
209 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
210 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
211 zlog_debug("%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
212 __FILE__, __PRETTY_FUNCTION__,
213 oif->name, pim_ifp->mroute_vif_index,
214 source_str, group_str);
215 }
216 return -1;
217 }
218
219 return 0;
220 }
221
222
223 int pim_channel_add_oif(struct channel_oil *channel_oil,
224 struct interface *oif,
225 uint32_t proto_mask)
226 {
227 struct pim_interface *pim_ifp;
228 int old_ttl;
229
230 zassert(channel_oil);
231
232 pim_ifp = oif->info;
233
234 if (PIM_DEBUG_MROUTE) {
235 char group_str[100];
236 char source_str[100];
237 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
238 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
239 zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d",
240 __FILE__, __PRETTY_FUNCTION__,
241 source_str, group_str,
242 proto_mask, oif->name, pim_ifp->mroute_vif_index);
243 }
244
245 #ifdef PIM_ENFORCE_LOOPFREE_MFC
246 /*
247 Prevent creating MFC entry with OIF=IIF.
248
249 This is a protection against implementation mistakes.
250
251 PIM protocol implicitely ensures loopfree multicast topology.
252
253 IGMP must be protected against adding looped MFC entries created
254 by both source and receiver attached to the same interface. See
255 TODO T22.
256 */
257 if (pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) {
258 if (PIM_DEBUG_MROUTE)
259 {
260 char group_str[100];
261 char source_str[100];
262 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
263 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
264 zlog_debug("%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
265 __FILE__, __PRETTY_FUNCTION__,
266 proto_mask, oif->name, pim_ifp->mroute_vif_index,
267 source_str, group_str);
268 }
269 return -2;
270 }
271 #endif
272
273 /* Prevent single protocol from subscribing same interface to
274 channel (S,G) multiple times */
275 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
276 if (PIM_DEBUG_MROUTE)
277 {
278 char group_str[100];
279 char source_str[100];
280 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
281 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
282 zlog_debug("%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
283 __FILE__, __PRETTY_FUNCTION__,
284 proto_mask, oif->name, pim_ifp->mroute_vif_index,
285 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
286 source_str, group_str);
287 }
288 return -3;
289 }
290
291 /* Allow other protocol to request subscription of same interface to
292 channel (S,G) multiple times, by silently ignoring further
293 requests */
294 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & PIM_OIF_FLAG_PROTO_ANY) {
295
296 /* Check the OIF really exists before returning, and only log
297 warning otherwise */
298 if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
299 if (PIM_DEBUG_MROUTE)
300 {
301 char group_str[100];
302 char source_str[100];
303 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
304 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
305 zlog_debug("%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
306 __FILE__, __PRETTY_FUNCTION__,
307 proto_mask, oif->name, pim_ifp->mroute_vif_index,
308 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
309 source_str, group_str);
310 }
311 }
312
313 return 0;
314 }
315
316 old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index];
317
318 if (old_ttl > 0) {
319 if (PIM_DEBUG_MROUTE)
320 {
321 char group_str[100];
322 char source_str[100];
323 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
324 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
325 zlog_debug("%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
326 __FILE__, __PRETTY_FUNCTION__,
327 oif->name, pim_ifp->mroute_vif_index,
328 source_str, group_str);
329 }
330 return -4;
331 }
332
333 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = PIM_MROUTE_MIN_TTL;
334
335 if (pim_mroute_add(channel_oil)) {
336 if (PIM_DEBUG_MROUTE)
337 {
338 char group_str[100];
339 char source_str[100];
340 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
341 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
342 zlog_debug("%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
343 __FILE__, __PRETTY_FUNCTION__,
344 oif->name, pim_ifp->mroute_vif_index,
345 source_str, group_str);
346 }
347
348 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl;
349 return -5;
350 }
351
352 channel_oil->oif_creation[pim_ifp->mroute_vif_index] = pim_time_monotonic_sec();
353 ++channel_oil->oil_size;
354 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
355
356 if (PIM_DEBUG_MROUTE) {
357 char group_str[100];
358 char source_str[100];
359 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
360 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
361 zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
362 __FILE__, __PRETTY_FUNCTION__,
363 source_str, group_str,
364 proto_mask, oif->name, pim_ifp->mroute_vif_index);
365 }
366
367 return 0;
368 }