]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_oil.c
Merge pull request #805 from Orange-OpenSource/master
[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 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 "log.h"
23 #include "memory.h"
24 #include "linklist.h"
25 #include "if.h"
26 #include "hash.h"
27 #include "jhash.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 struct list *pim_channel_oil_list = NULL;
36 struct hash *pim_channel_oil_hash = NULL;
37
38 char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size)
39 {
40 struct prefix_sg sg;
41 int i;
42
43 memset(buf, 0, size);
44 sg.src = c_oil->oil.mfcc_origin;
45 sg.grp = c_oil->oil.mfcc_mcastgrp;
46 sprintf(buf, "%s IIF: %d, OIFS: ", pim_str_sg_dump(&sg),
47 c_oil->oil.mfcc_parent);
48
49 for (i = 0; i < MAXVIFS; i++) {
50 if (c_oil->oil.mfcc_ttls[i] != 0) {
51 char buf1[10];
52 sprintf(buf1, "%d ", i);
53 strcat(buf, buf1);
54 }
55 }
56
57 return buf;
58 }
59
60 static int pim_channel_oil_compare(struct channel_oil *c1,
61 struct channel_oil *c2)
62 {
63 if (ntohl(c1->oil.mfcc_mcastgrp.s_addr)
64 < ntohl(c2->oil.mfcc_mcastgrp.s_addr))
65 return -1;
66
67 if (ntohl(c1->oil.mfcc_mcastgrp.s_addr)
68 > ntohl(c2->oil.mfcc_mcastgrp.s_addr))
69 return 1;
70
71 if (ntohl(c1->oil.mfcc_origin.s_addr)
72 < ntohl(c2->oil.mfcc_origin.s_addr))
73 return -1;
74
75 if (ntohl(c1->oil.mfcc_origin.s_addr)
76 > ntohl(c2->oil.mfcc_origin.s_addr))
77 return 1;
78
79 return 0;
80 }
81
82 static int pim_oil_equal(const void *arg1, const void *arg2)
83 {
84 const struct channel_oil *c1 = (const struct channel_oil *)arg1;
85 const struct channel_oil *c2 = (const struct channel_oil *)arg2;
86
87 if ((c1->oil.mfcc_mcastgrp.s_addr == c2->oil.mfcc_mcastgrp.s_addr)
88 && (c1->oil.mfcc_origin.s_addr == c2->oil.mfcc_origin.s_addr))
89 return 1;
90
91 return 0;
92 }
93
94 static unsigned int pim_oil_hash_key(void *arg)
95 {
96 struct channel_oil *oil = (struct channel_oil *)arg;
97
98 return jhash_2words(oil->oil.mfcc_mcastgrp.s_addr,
99 oil->oil.mfcc_origin.s_addr, 0);
100 }
101
102 void pim_oil_init(void)
103 {
104 pim_channel_oil_hash =
105 hash_create_size(8192, pim_oil_hash_key, pim_oil_equal, NULL);
106
107 pim_channel_oil_list = list_new();
108 if (!pim_channel_oil_list) {
109 zlog_err("%s %s: failure: channel_oil_list=list_new()",
110 __FILE__, __PRETTY_FUNCTION__);
111 return;
112 }
113 pim_channel_oil_list->del = (void (*)(void *))pim_channel_oil_free;
114 pim_channel_oil_list->cmp =
115 (int (*)(void *, void *))pim_channel_oil_compare;
116 }
117
118 void pim_oil_terminate(void)
119 {
120 if (pim_channel_oil_list)
121 list_free(pim_channel_oil_list);
122 pim_channel_oil_list = NULL;
123
124 if (pim_channel_oil_hash)
125 hash_free(pim_channel_oil_hash);
126 pim_channel_oil_hash = NULL;
127 }
128
129 void pim_channel_oil_free(struct channel_oil *c_oil)
130 {
131 XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
132 }
133
134 static struct channel_oil *pim_find_channel_oil(struct prefix_sg *sg)
135 {
136 struct channel_oil *c_oil = NULL;
137 struct channel_oil lookup;
138
139 lookup.oil.mfcc_mcastgrp = sg->grp;
140 lookup.oil.mfcc_origin = sg->src;
141
142 c_oil = hash_lookup(pim_channel_oil_hash, &lookup);
143
144 return c_oil;
145 }
146
147 struct channel_oil *pim_channel_oil_add(struct prefix_sg *sg,
148 int input_vif_index)
149 {
150 struct channel_oil *c_oil;
151 struct interface *ifp;
152
153 c_oil = pim_find_channel_oil(sg);
154 if (c_oil) {
155 if (c_oil->oil.mfcc_parent != input_vif_index) {
156 c_oil->oil_inherited_rescan = 1;
157 if (PIM_DEBUG_MROUTE)
158 zlog_debug(
159 "%s: Existing channel oil %s points to %d, modifying to point at %d",
160 __PRETTY_FUNCTION__,
161 pim_str_sg_dump(sg),
162 c_oil->oil.mfcc_parent,
163 input_vif_index);
164 }
165 c_oil->oil.mfcc_parent = input_vif_index;
166 ++c_oil->oil_ref_count;
167 c_oil->up = pim_upstream_find(
168 sg); // channel might be present prior to upstream
169 return c_oil;
170 }
171
172 ifp = pim_if_find_by_vif_index(input_vif_index);
173 if (!ifp) {
174 /* warning only */
175 zlog_warn(
176 "%s: (S,G)=%s could not find input interface for input_vif_index=%d",
177 __PRETTY_FUNCTION__, pim_str_sg_dump(sg),
178 input_vif_index);
179 }
180
181 c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
182 if (!c_oil) {
183 zlog_err("PIM XCALLOC(%zu) failure", sizeof(*c_oil));
184 return NULL;
185 }
186
187 c_oil->oil.mfcc_mcastgrp = sg->grp;
188 c_oil->oil.mfcc_origin = sg->src;
189 c_oil = hash_get(pim_channel_oil_hash, c_oil, hash_alloc_intern);
190
191 c_oil->oil.mfcc_parent = input_vif_index;
192 c_oil->oil_ref_count = 1;
193 c_oil->installed = 0;
194 c_oil->up = pim_upstream_find(sg);
195
196 listnode_add_sort(pim_channel_oil_list, c_oil);
197
198 return c_oil;
199 }
200
201 void pim_channel_oil_del(struct channel_oil *c_oil)
202 {
203 --c_oil->oil_ref_count;
204
205 if (c_oil->oil_ref_count < 1) {
206 /*
207 * notice that listnode_delete() can't be moved
208 * into pim_channel_oil_free() because the later is
209 * called by list_delete_all_node()
210 */
211 c_oil->up = NULL;
212 listnode_delete(pim_channel_oil_list, c_oil);
213 hash_release(pim_channel_oil_hash, c_oil);
214
215 pim_channel_oil_free(c_oil);
216 }
217 }
218
219 int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
220 uint32_t proto_mask)
221 {
222 struct pim_interface *pim_ifp;
223
224 zassert(channel_oil);
225 zassert(oif);
226
227 pim_ifp = oif->info;
228
229 /*
230 * Don't do anything if we've been asked to remove a source
231 * that is not actually on it.
232 */
233 if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) {
234 if (PIM_DEBUG_MROUTE) {
235 char group_str[INET_ADDRSTRLEN];
236 char source_str[INET_ADDRSTRLEN];
237 pim_inet4_dump("<group?>",
238 channel_oil->oil.mfcc_mcastgrp,
239 group_str, sizeof(group_str));
240 pim_inet4_dump("<source?>",
241 channel_oil->oil.mfcc_origin, source_str,
242 sizeof(source_str));
243 zlog_debug(
244 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
245 __FILE__, __PRETTY_FUNCTION__, proto_mask,
246 channel_oil
247 ->oif_flags[pim_ifp->mroute_vif_index],
248 oif->name, pim_ifp->mroute_vif_index,
249 channel_oil->oil
250 .mfcc_ttls[pim_ifp->mroute_vif_index],
251 source_str, group_str);
252 }
253 return 0;
254 }
255
256 channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
257
258 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]) {
259 if (PIM_DEBUG_MROUTE) {
260 char group_str[INET_ADDRSTRLEN];
261 char source_str[INET_ADDRSTRLEN];
262 pim_inet4_dump("<group?>",
263 channel_oil->oil.mfcc_mcastgrp,
264 group_str, sizeof(group_str));
265 pim_inet4_dump("<source?>",
266 channel_oil->oil.mfcc_origin, source_str,
267 sizeof(source_str));
268 zlog_debug(
269 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
270 __FILE__, __PRETTY_FUNCTION__, oif->name,
271 pim_ifp->mroute_vif_index,
272 channel_oil->oil
273 .mfcc_ttls[pim_ifp->mroute_vif_index],
274 source_str, group_str);
275 }
276 return 0;
277 }
278
279 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0;
280
281 if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
282 if (PIM_DEBUG_MROUTE) {
283 char group_str[INET_ADDRSTRLEN];
284 char source_str[INET_ADDRSTRLEN];
285 pim_inet4_dump("<group?>",
286 channel_oil->oil.mfcc_mcastgrp,
287 group_str, sizeof(group_str));
288 pim_inet4_dump("<source?>",
289 channel_oil->oil.mfcc_origin, source_str,
290 sizeof(source_str));
291 zlog_debug(
292 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
293 __FILE__, __PRETTY_FUNCTION__, oif->name,
294 pim_ifp->mroute_vif_index, source_str,
295 group_str);
296 }
297 return -1;
298 }
299
300 --channel_oil->oil_size;
301
302 if (PIM_DEBUG_MROUTE) {
303 char group_str[INET_ADDRSTRLEN];
304 char source_str[INET_ADDRSTRLEN];
305 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp,
306 group_str, sizeof(group_str));
307 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin,
308 source_str, sizeof(source_str));
309 zlog_debug(
310 "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
311 __FILE__, __PRETTY_FUNCTION__, source_str, group_str,
312 proto_mask, channel_oil->oil.mfcc_parent, oif->name,
313 pim_ifp->mroute_vif_index);
314 }
315
316 return 0;
317 }
318
319
320 int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
321 uint32_t proto_mask)
322 {
323 struct pim_interface *pim_ifp;
324 int old_ttl;
325
326 /*
327 * If we've gotten here we've gone bad, but let's
328 * not take down pim
329 */
330 if (!channel_oil) {
331 zlog_warn("Attempt to Add OIF for non-existent channel oil");
332 return -1;
333 }
334
335 pim_ifp = oif->info;
336
337 #ifdef PIM_ENFORCE_LOOPFREE_MFC
338 /*
339 Prevent creating MFC entry with OIF=IIF.
340
341 This is a protection against implementation mistakes.
342
343 PIM protocol implicitely ensures loopfree multicast topology.
344
345 IGMP must be protected against adding looped MFC entries created
346 by both source and receiver attached to the same interface. See
347 TODO T22.
348 */
349 if (pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) {
350 channel_oil->oil_inherited_rescan = 1;
351 if (PIM_DEBUG_MROUTE) {
352 char group_str[INET_ADDRSTRLEN];
353 char source_str[INET_ADDRSTRLEN];
354 pim_inet4_dump("<group?>",
355 channel_oil->oil.mfcc_mcastgrp,
356 group_str, sizeof(group_str));
357 pim_inet4_dump("<source?>",
358 channel_oil->oil.mfcc_origin, source_str,
359 sizeof(source_str));
360 zlog_debug(
361 "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
362 __FILE__, __PRETTY_FUNCTION__, proto_mask,
363 oif->name, pim_ifp->mroute_vif_index,
364 source_str, group_str);
365 }
366 return -2;
367 }
368 #endif
369
370 /* Prevent single protocol from subscribing same interface to
371 channel (S,G) multiple times */
372 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
373 if (PIM_DEBUG_MROUTE) {
374 char group_str[INET_ADDRSTRLEN];
375 char source_str[INET_ADDRSTRLEN];
376 pim_inet4_dump("<group?>",
377 channel_oil->oil.mfcc_mcastgrp,
378 group_str, sizeof(group_str));
379 pim_inet4_dump("<source?>",
380 channel_oil->oil.mfcc_origin, source_str,
381 sizeof(source_str));
382 zlog_debug(
383 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
384 __FILE__, __PRETTY_FUNCTION__, proto_mask,
385 oif->name, pim_ifp->mroute_vif_index,
386 channel_oil->oil
387 .mfcc_ttls[pim_ifp->mroute_vif_index],
388 source_str, group_str);
389 }
390 return -3;
391 }
392
393 /* Allow other protocol to request subscription of same interface to
394 * channel (S,G), we need to note this information
395 */
396 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]
397 & PIM_OIF_FLAG_PROTO_ANY) {
398
399 channel_oil->oif_creation[pim_ifp->mroute_vif_index] =
400 pim_time_monotonic_sec();
401 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
402 /* Check the OIF really exists before returning, and only log
403 warning otherwise */
404 if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
405 {
406 char group_str[INET_ADDRSTRLEN];
407 char source_str[INET_ADDRSTRLEN];
408 pim_inet4_dump("<group?>",
409 channel_oil->oil.mfcc_mcastgrp,
410 group_str, sizeof(group_str));
411 pim_inet4_dump("<source?>",
412 channel_oil->oil.mfcc_origin,
413 source_str, sizeof(source_str));
414 zlog_warn(
415 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
416 __FILE__, __PRETTY_FUNCTION__,
417 proto_mask, oif->name,
418 pim_ifp->mroute_vif_index,
419 channel_oil->oil.mfcc_ttls
420 [pim_ifp->mroute_vif_index],
421 source_str, group_str);
422 }
423 }
424
425 return 0;
426 }
427
428 old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index];
429
430 if (old_ttl > 0) {
431 if (PIM_DEBUG_MROUTE) {
432 char group_str[INET_ADDRSTRLEN];
433 char source_str[INET_ADDRSTRLEN];
434 pim_inet4_dump("<group?>",
435 channel_oil->oil.mfcc_mcastgrp,
436 group_str, sizeof(group_str));
437 pim_inet4_dump("<source?>",
438 channel_oil->oil.mfcc_origin, source_str,
439 sizeof(source_str));
440 zlog_debug(
441 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
442 __FILE__, __PRETTY_FUNCTION__, oif->name,
443 pim_ifp->mroute_vif_index, source_str,
444 group_str);
445 }
446 return -4;
447 }
448
449 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] =
450 PIM_MROUTE_MIN_TTL;
451
452 if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
453 if (PIM_DEBUG_MROUTE) {
454 char group_str[INET_ADDRSTRLEN];
455 char source_str[INET_ADDRSTRLEN];
456 pim_inet4_dump("<group?>",
457 channel_oil->oil.mfcc_mcastgrp,
458 group_str, sizeof(group_str));
459 pim_inet4_dump("<source?>",
460 channel_oil->oil.mfcc_origin, source_str,
461 sizeof(source_str));
462 zlog_debug(
463 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
464 __FILE__, __PRETTY_FUNCTION__, oif->name,
465 pim_ifp->mroute_vif_index, source_str,
466 group_str);
467 }
468
469 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl;
470 return -5;
471 }
472
473 channel_oil->oif_creation[pim_ifp->mroute_vif_index] =
474 pim_time_monotonic_sec();
475 ++channel_oil->oil_size;
476 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
477
478 if (PIM_DEBUG_MROUTE) {
479 char group_str[INET_ADDRSTRLEN];
480 char source_str[INET_ADDRSTRLEN];
481 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp,
482 group_str, sizeof(group_str));
483 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin,
484 source_str, sizeof(source_str));
485 zlog_debug(
486 "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
487 __FILE__, __PRETTY_FUNCTION__, source_str, group_str,
488 proto_mask, oif->name, pim_ifp->mroute_vif_index);
489 }
490
491 return 0;
492 }
493
494 int pim_channel_oil_empty(struct channel_oil *c_oil)
495 {
496 static uint32_t zero[MAXVIFS];
497 static int inited = 0;
498
499 if (!c_oil)
500 return 1;
501 /*
502 * Not sure that this is necessary, but I would rather ensure
503 * that this works.
504 */
505 if (!inited) {
506 memset(&zero, 0, sizeof(uint32_t) * MAXVIFS);
507 inited = 1;
508 }
509
510 return !memcmp(c_oil->oif_flags, zero, MAXVIFS * sizeof(uint32_t));
511 }