]>
git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_oil.c
3 * Copyright (C) 2008 Everton da Silva Marques
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.
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.
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
32 #include "pim_iface.h"
35 // struct list *pim_channel_oil_list = NULL;
36 // struct hash *pim_channel_oil_hash = NULL;
38 char *pim_channel_oil_dump(struct channel_oil
*c_oil
, char *buf
, size_t 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
);
49 for (i
= 0; i
< MAXVIFS
; i
++) {
50 if (c_oil
->oil
.mfcc_ttls
[i
] != 0) {
52 sprintf(buf1
, "%d ", i
);
60 static int pim_channel_oil_compare(struct channel_oil
*c1
,
61 struct channel_oil
*c2
)
63 if (ntohl(c1
->oil
.mfcc_mcastgrp
.s_addr
)
64 < ntohl(c2
->oil
.mfcc_mcastgrp
.s_addr
))
67 if (ntohl(c1
->oil
.mfcc_mcastgrp
.s_addr
)
68 > ntohl(c2
->oil
.mfcc_mcastgrp
.s_addr
))
71 if (ntohl(c1
->oil
.mfcc_origin
.s_addr
)
72 < ntohl(c2
->oil
.mfcc_origin
.s_addr
))
75 if (ntohl(c1
->oil
.mfcc_origin
.s_addr
)
76 > ntohl(c2
->oil
.mfcc_origin
.s_addr
))
82 static int pim_oil_equal(const void *arg1
, const void *arg2
)
84 const struct channel_oil
*c1
= (const struct channel_oil
*)arg1
;
85 const struct channel_oil
*c2
= (const struct channel_oil
*)arg2
;
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
))
94 static unsigned int pim_oil_hash_key(void *arg
)
96 struct channel_oil
*oil
= (struct channel_oil
*)arg
;
98 return jhash_2words(oil
->oil
.mfcc_mcastgrp
.s_addr
,
99 oil
->oil
.mfcc_origin
.s_addr
, 0);
102 void pim_oil_init(struct pim_instance
*pim
)
106 snprintf(hash_name
, 64, "PIM %s Oil Hash", pim
->vrf
->name
);
107 pim
->channel_oil_hash
= hash_create_size(8192,
112 pim
->channel_oil_list
= list_new();
113 if (!pim
->channel_oil_list
) {
114 zlog_err("%s %s: failure: channel_oil_list=list_new()",
115 __FILE__
, __PRETTY_FUNCTION__
);
118 pim
->channel_oil_list
->del
= (void (*)(void *))pim_channel_oil_free
;
119 pim
->channel_oil_list
->cmp
=
120 (int (*)(void *, void *))pim_channel_oil_compare
;
123 void pim_oil_terminate(struct pim_instance
*pim
)
125 if (pim
->channel_oil_list
)
126 list_delete(pim
->channel_oil_list
);
127 pim
->channel_oil_list
= NULL
;
129 if (pim
->channel_oil_hash
)
130 hash_free(pim
->channel_oil_hash
);
131 pim
->channel_oil_hash
= NULL
;
134 void pim_channel_oil_free(struct channel_oil
*c_oil
)
136 XFREE(MTYPE_PIM_CHANNEL_OIL
, c_oil
);
139 static struct channel_oil
*pim_find_channel_oil(struct pim_instance
*pim
,
140 struct prefix_sg
*sg
)
142 struct channel_oil
*c_oil
= NULL
;
143 struct channel_oil lookup
;
145 lookup
.oil
.mfcc_mcastgrp
= sg
->grp
;
146 lookup
.oil
.mfcc_origin
= sg
->src
;
148 c_oil
= hash_lookup(pim
->channel_oil_hash
, &lookup
);
153 struct channel_oil
*pim_channel_oil_add(struct pim_instance
*pim
,
154 struct prefix_sg
*sg
,
157 struct channel_oil
*c_oil
;
158 struct interface
*ifp
;
160 c_oil
= pim_find_channel_oil(pim
, sg
);
162 if (c_oil
->oil
.mfcc_parent
!= input_vif_index
) {
163 c_oil
->oil_inherited_rescan
= 1;
164 if (PIM_DEBUG_MROUTE
)
166 "%s: Existing channel oil %s points to %d, modifying to point at %d",
169 c_oil
->oil
.mfcc_parent
,
172 c_oil
->oil
.mfcc_parent
= input_vif_index
;
173 ++c_oil
->oil_ref_count
;
174 c_oil
->up
= pim_upstream_find(
175 pim
, sg
); // channel might be present prior to upstream
179 ifp
= pim_if_find_by_vif_index(pim
, input_vif_index
);
183 "%s: (S,G)=%s could not find input interface for input_vif_index=%d",
184 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
),
188 c_oil
= XCALLOC(MTYPE_PIM_CHANNEL_OIL
, sizeof(*c_oil
));
190 zlog_err("PIM XCALLOC(%zu) failure", sizeof(*c_oil
));
194 c_oil
->oil
.mfcc_mcastgrp
= sg
->grp
;
195 c_oil
->oil
.mfcc_origin
= sg
->src
;
196 c_oil
= hash_get(pim
->channel_oil_hash
, c_oil
, hash_alloc_intern
);
198 c_oil
->oil
.mfcc_parent
= input_vif_index
;
199 c_oil
->oil_ref_count
= 1;
200 c_oil
->installed
= 0;
201 c_oil
->up
= pim_upstream_find(pim
, sg
);
204 listnode_add_sort(pim
->channel_oil_list
, c_oil
);
209 void pim_channel_oil_del(struct channel_oil
*c_oil
)
211 --c_oil
->oil_ref_count
;
213 if (c_oil
->oil_ref_count
< 1) {
215 * notice that listnode_delete() can't be moved
216 * into pim_channel_oil_free() because the later is
217 * called by list_delete_all_node()
220 listnode_delete(c_oil
->pim
->channel_oil_list
, c_oil
);
221 hash_release(c_oil
->pim
->channel_oil_hash
, c_oil
);
223 pim_channel_oil_free(c_oil
);
227 int pim_channel_del_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
230 struct pim_interface
*pim_ifp
;
232 zassert(channel_oil
);
238 * Don't do anything if we've been asked to remove a source
239 * that is not actually on it.
241 if (!(channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
)) {
242 if (PIM_DEBUG_MROUTE
) {
243 char group_str
[INET_ADDRSTRLEN
];
244 char source_str
[INET_ADDRSTRLEN
];
245 pim_inet4_dump("<group?>",
246 channel_oil
->oil
.mfcc_mcastgrp
,
247 group_str
, sizeof(group_str
));
248 pim_inet4_dump("<source?>",
249 channel_oil
->oil
.mfcc_origin
, source_str
,
252 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
253 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
255 ->oif_flags
[pim_ifp
->mroute_vif_index
],
256 oif
->name
, pim_ifp
->mroute_vif_index
,
258 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
259 source_str
, group_str
);
264 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~proto_mask
;
266 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]) {
267 if (PIM_DEBUG_MROUTE
) {
268 char group_str
[INET_ADDRSTRLEN
];
269 char source_str
[INET_ADDRSTRLEN
];
270 pim_inet4_dump("<group?>",
271 channel_oil
->oil
.mfcc_mcastgrp
,
272 group_str
, sizeof(group_str
));
273 pim_inet4_dump("<source?>",
274 channel_oil
->oil
.mfcc_origin
, source_str
,
277 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
278 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
279 pim_ifp
->mroute_vif_index
,
281 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
282 source_str
, group_str
);
287 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = 0;
289 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
290 if (PIM_DEBUG_MROUTE
) {
291 char group_str
[INET_ADDRSTRLEN
];
292 char source_str
[INET_ADDRSTRLEN
];
293 pim_inet4_dump("<group?>",
294 channel_oil
->oil
.mfcc_mcastgrp
,
295 group_str
, sizeof(group_str
));
296 pim_inet4_dump("<source?>",
297 channel_oil
->oil
.mfcc_origin
, source_str
,
300 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
301 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
302 pim_ifp
->mroute_vif_index
, source_str
,
308 --channel_oil
->oil_size
;
310 if (PIM_DEBUG_MROUTE
) {
311 char group_str
[INET_ADDRSTRLEN
];
312 char source_str
[INET_ADDRSTRLEN
];
313 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
314 group_str
, sizeof(group_str
));
315 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
316 source_str
, sizeof(source_str
));
318 "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
319 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
320 proto_mask
, channel_oil
->oil
.mfcc_parent
, oif
->name
,
321 pim_ifp
->mroute_vif_index
);
328 int pim_channel_add_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
331 struct pim_interface
*pim_ifp
;
335 * If we've gotten here we've gone bad, but let's
339 zlog_warn("Attempt to Add OIF for non-existent channel oil");
345 #ifdef PIM_ENFORCE_LOOPFREE_MFC
347 Prevent creating MFC entry with OIF=IIF.
349 This is a protection against implementation mistakes.
351 PIM protocol implicitely ensures loopfree multicast topology.
353 IGMP must be protected against adding looped MFC entries created
354 by both source and receiver attached to the same interface. See
357 if (pim_ifp
->mroute_vif_index
== channel_oil
->oil
.mfcc_parent
) {
358 channel_oil
->oil_inherited_rescan
= 1;
359 if (PIM_DEBUG_MROUTE
) {
360 char group_str
[INET_ADDRSTRLEN
];
361 char source_str
[INET_ADDRSTRLEN
];
362 pim_inet4_dump("<group?>",
363 channel_oil
->oil
.mfcc_mcastgrp
,
364 group_str
, sizeof(group_str
));
365 pim_inet4_dump("<source?>",
366 channel_oil
->oil
.mfcc_origin
, source_str
,
369 "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
370 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
371 oif
->name
, pim_ifp
->mroute_vif_index
,
372 source_str
, group_str
);
378 /* Prevent single protocol from subscribing same interface to
379 channel (S,G) multiple times */
380 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
) {
381 if (PIM_DEBUG_MROUTE
) {
382 char group_str
[INET_ADDRSTRLEN
];
383 char source_str
[INET_ADDRSTRLEN
];
384 pim_inet4_dump("<group?>",
385 channel_oil
->oil
.mfcc_mcastgrp
,
386 group_str
, sizeof(group_str
));
387 pim_inet4_dump("<source?>",
388 channel_oil
->oil
.mfcc_origin
, source_str
,
391 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
392 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
393 oif
->name
, pim_ifp
->mroute_vif_index
,
395 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
396 source_str
, group_str
);
401 /* Allow other protocol to request subscription of same interface to
402 * channel (S,G), we need to note this information
404 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]
405 & PIM_OIF_FLAG_PROTO_ANY
) {
407 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
408 pim_time_monotonic_sec();
409 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
410 /* Check the OIF really exists before returning, and only log
412 if (channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] < 1) {
414 char group_str
[INET_ADDRSTRLEN
];
415 char source_str
[INET_ADDRSTRLEN
];
416 pim_inet4_dump("<group?>",
417 channel_oil
->oil
.mfcc_mcastgrp
,
418 group_str
, sizeof(group_str
));
419 pim_inet4_dump("<source?>",
420 channel_oil
->oil
.mfcc_origin
,
421 source_str
, sizeof(source_str
));
423 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
424 __FILE__
, __PRETTY_FUNCTION__
,
425 proto_mask
, oif
->name
,
426 pim_ifp
->mroute_vif_index
,
427 channel_oil
->oil
.mfcc_ttls
428 [pim_ifp
->mroute_vif_index
],
429 source_str
, group_str
);
436 old_ttl
= channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
];
439 if (PIM_DEBUG_MROUTE
) {
440 char group_str
[INET_ADDRSTRLEN
];
441 char source_str
[INET_ADDRSTRLEN
];
442 pim_inet4_dump("<group?>",
443 channel_oil
->oil
.mfcc_mcastgrp
,
444 group_str
, sizeof(group_str
));
445 pim_inet4_dump("<source?>",
446 channel_oil
->oil
.mfcc_origin
, source_str
,
449 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
450 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
451 pim_ifp
->mroute_vif_index
, source_str
,
457 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] =
460 if (pim_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
461 if (PIM_DEBUG_MROUTE
) {
462 char group_str
[INET_ADDRSTRLEN
];
463 char source_str
[INET_ADDRSTRLEN
];
464 pim_inet4_dump("<group?>",
465 channel_oil
->oil
.mfcc_mcastgrp
,
466 group_str
, sizeof(group_str
));
467 pim_inet4_dump("<source?>",
468 channel_oil
->oil
.mfcc_origin
, source_str
,
471 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
472 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
473 pim_ifp
->mroute_vif_index
, source_str
,
477 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = old_ttl
;
481 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
482 pim_time_monotonic_sec();
483 ++channel_oil
->oil_size
;
484 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
486 if (PIM_DEBUG_MROUTE
) {
487 char group_str
[INET_ADDRSTRLEN
];
488 char source_str
[INET_ADDRSTRLEN
];
489 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
490 group_str
, sizeof(group_str
));
491 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
492 source_str
, sizeof(source_str
));
494 "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
495 __FILE__
, __PRETTY_FUNCTION__
, source_str
, group_str
,
496 proto_mask
, oif
->name
, pim_ifp
->mroute_vif_index
);
502 int pim_channel_oil_empty(struct channel_oil
*c_oil
)
504 static uint32_t zero
[MAXVIFS
];
505 static int inited
= 0;
510 * Not sure that this is necessary, but I would rather ensure
514 memset(&zero
, 0, sizeof(uint32_t) * MAXVIFS
);
518 return !memcmp(c_oil
->oif_flags
, zero
, MAXVIFS
* sizeof(uint32_t));