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