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