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