]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_oil.c
Merge pull request #5498 from mjstapp/sharp_with_labels
[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 #include "pim_vxlan.h"
35
36 // struct list *pim_channel_oil_list = NULL;
37 // struct hash *pim_channel_oil_hash = NULL;
38
39 static void pim_channel_update_mute(struct channel_oil *c_oil);
40
41 char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size)
42 {
43 char *out;
44 struct interface *ifp;
45 struct prefix_sg sg;
46 int i;
47
48 sg.src = c_oil->oil.mfcc_origin;
49 sg.grp = c_oil->oil.mfcc_mcastgrp;
50 ifp = pim_if_find_by_vif_index(c_oil->pim, c_oil->oil.mfcc_parent);
51 snprintf(buf, size, "%s IIF: %s, OIFS: ", pim_str_sg_dump(&sg),
52 ifp ? ifp->name : "(?)");
53
54 out = buf + strlen(buf);
55 for (i = 0; i < MAXVIFS; i++) {
56 if (c_oil->oil.mfcc_ttls[i] != 0) {
57 ifp = pim_if_find_by_vif_index(c_oil->pim, i);
58 snprintf(out, buf + size - out, "%s ",
59 ifp ? ifp->name : "(?)");
60 out += strlen(out);
61 }
62 }
63
64 return buf;
65 }
66
67 static int pim_channel_oil_compare(struct channel_oil *c1,
68 struct channel_oil *c2)
69 {
70 if (ntohl(c1->oil.mfcc_mcastgrp.s_addr)
71 < ntohl(c2->oil.mfcc_mcastgrp.s_addr))
72 return -1;
73
74 if (ntohl(c1->oil.mfcc_mcastgrp.s_addr)
75 > ntohl(c2->oil.mfcc_mcastgrp.s_addr))
76 return 1;
77
78 if (ntohl(c1->oil.mfcc_origin.s_addr)
79 < ntohl(c2->oil.mfcc_origin.s_addr))
80 return -1;
81
82 if (ntohl(c1->oil.mfcc_origin.s_addr)
83 > ntohl(c2->oil.mfcc_origin.s_addr))
84 return 1;
85
86 return 0;
87 }
88
89 static bool pim_oil_equal(const void *arg1, const void *arg2)
90 {
91 const struct channel_oil *c1 = (const struct channel_oil *)arg1;
92 const struct channel_oil *c2 = (const struct channel_oil *)arg2;
93
94 if ((c1->oil.mfcc_mcastgrp.s_addr == c2->oil.mfcc_mcastgrp.s_addr)
95 && (c1->oil.mfcc_origin.s_addr == c2->oil.mfcc_origin.s_addr))
96 return true;
97
98 return false;
99 }
100
101 static unsigned int pim_oil_hash_key(const void *arg)
102 {
103 const struct channel_oil *oil = arg;
104
105 return jhash_2words(oil->oil.mfcc_mcastgrp.s_addr,
106 oil->oil.mfcc_origin.s_addr, 0);
107 }
108
109 void pim_oil_init(struct pim_instance *pim)
110 {
111 char hash_name[64];
112
113 snprintf(hash_name, 64, "PIM %s Oil Hash", pim->vrf->name);
114 pim->channel_oil_hash = hash_create_size(8192, pim_oil_hash_key,
115 pim_oil_equal, hash_name);
116
117 pim->channel_oil_list = list_new();
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;
121 }
122
123 void pim_oil_terminate(struct pim_instance *pim)
124 {
125 if (pim->channel_oil_list)
126 list_delete(&pim->channel_oil_list);
127
128 if (pim->channel_oil_hash)
129 hash_free(pim->channel_oil_hash);
130 pim->channel_oil_hash = NULL;
131 }
132
133 void pim_channel_oil_free(struct channel_oil *c_oil)
134 {
135 XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
136 }
137
138 struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
139 struct prefix_sg *sg)
140 {
141 struct channel_oil *c_oil = NULL;
142 struct channel_oil lookup;
143
144 lookup.oil.mfcc_mcastgrp = sg->grp;
145 lookup.oil.mfcc_origin = sg->src;
146
147 c_oil = hash_lookup(pim->channel_oil_hash, &lookup);
148
149 return c_oil;
150 }
151
152 struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
153 struct prefix_sg *sg,
154 const char *name)
155 {
156 struct channel_oil *c_oil;
157
158 c_oil = pim_find_channel_oil(pim, sg);
159 if (c_oil) {
160 ++c_oil->oil_ref_count;
161
162 if (!c_oil->up) {
163 /* channel might be present prior to upstream */
164 c_oil->up = pim_upstream_find(
165 pim, sg);
166 /* if the upstream entry is being anchored to an
167 * already existing channel OIL we need to re-evaluate
168 * the "Mute" state on AA OIFs
169 */
170 pim_channel_update_mute(c_oil);
171 }
172
173 /* check if the IIF has changed
174 * XXX - is this really needed
175 */
176 pim_upstream_mroute_iif_update(c_oil, __func__);
177
178 if (PIM_DEBUG_MROUTE)
179 zlog_debug(
180 "%s(%s): Existing oil for %pSG4 Ref Count: %d (Post Increment)",
181 __PRETTY_FUNCTION__, name, sg,
182 c_oil->oil_ref_count);
183 return c_oil;
184 }
185
186 c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
187
188 c_oil->oil.mfcc_mcastgrp = sg->grp;
189 c_oil->oil.mfcc_origin = sg->src;
190 c_oil = hash_get(pim->channel_oil_hash, c_oil, hash_alloc_intern);
191
192 c_oil->oil.mfcc_parent = MAXVIFS;
193 c_oil->oil_ref_count = 1;
194 c_oil->installed = 0;
195 c_oil->up = pim_upstream_find(pim, sg);
196 c_oil->pim = pim;
197
198 listnode_add_sort(pim->channel_oil_list, c_oil);
199
200 if (PIM_DEBUG_MROUTE)
201 zlog_debug("%s(%s): c_oil %s add",
202 __func__, name, pim_str_sg_dump(sg));
203
204 return c_oil;
205 }
206
207 struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil,
208 const char *name)
209 {
210 if (PIM_DEBUG_MROUTE) {
211 struct prefix_sg sg = {.src = c_oil->oil.mfcc_mcastgrp,
212 .grp = c_oil->oil.mfcc_origin};
213
214 zlog_debug(
215 "%s(%s): Del oil for %pSG4, Ref Count: %d (Predecrement)",
216 __PRETTY_FUNCTION__, name, &sg, c_oil->oil_ref_count);
217 }
218 --c_oil->oil_ref_count;
219
220 if (c_oil->oil_ref_count < 1) {
221 /*
222 * notice that listnode_delete() can't be moved
223 * into pim_channel_oil_free() because the later is
224 * called by list_delete_all_node()
225 */
226 c_oil->up = NULL;
227 listnode_delete(c_oil->pim->channel_oil_list, c_oil);
228 hash_release(c_oil->pim->channel_oil_hash, c_oil);
229
230 pim_channel_oil_free(c_oil);
231 return NULL;
232 }
233
234 return c_oil;
235 }
236
237 void pim_channel_oil_upstream_deref(struct channel_oil *c_oil)
238 {
239 /* The upstream entry associated with a channel_oil is abt to be
240 * deleted. If the channel_oil is kept around because of other
241 * references we need to remove upstream based states out of it.
242 */
243 c_oil = pim_channel_oil_del(c_oil, __func__);
244 if (c_oil) {
245 /* note: here we assume that c_oil->up has already been
246 * cleared
247 */
248 pim_channel_update_mute(c_oil);
249 }
250 }
251
252 int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
253 uint32_t proto_mask, const char *caller)
254 {
255 struct pim_interface *pim_ifp;
256
257 zassert(channel_oil);
258 zassert(oif);
259
260 pim_ifp = oif->info;
261
262 /*
263 * Don't do anything if we've been asked to remove a source
264 * that is not actually on it.
265 */
266 if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) {
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,
275 sizeof(source_str));
276 zlog_debug(
277 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
278 __FILE__, __PRETTY_FUNCTION__, proto_mask,
279 channel_oil
280 ->oif_flags[pim_ifp->mroute_vif_index],
281 oif->name, pim_ifp->mroute_vif_index,
282 channel_oil->oil
283 .mfcc_ttls[pim_ifp->mroute_vif_index],
284 source_str, group_str);
285 }
286 return 0;
287 }
288
289 channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
290
291 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] &
292 PIM_OIF_FLAG_PROTO_ANY) {
293 if (PIM_DEBUG_MROUTE) {
294 char group_str[INET_ADDRSTRLEN];
295 char source_str[INET_ADDRSTRLEN];
296 pim_inet4_dump("<group?>",
297 channel_oil->oil.mfcc_mcastgrp,
298 group_str, sizeof(group_str));
299 pim_inet4_dump("<source?>",
300 channel_oil->oil.mfcc_origin, source_str,
301 sizeof(source_str));
302 zlog_debug(
303 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
304 __FILE__, __PRETTY_FUNCTION__, oif->name,
305 pim_ifp->mroute_vif_index,
306 channel_oil->oil
307 .mfcc_ttls[pim_ifp->mroute_vif_index],
308 source_str, group_str);
309 }
310 return 0;
311 }
312
313 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0;
314 /* clear mute; will be re-evaluated when the OIF becomes valid again */
315 channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~PIM_OIF_FLAG_MUTE;
316
317 if (pim_upstream_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
318 if (PIM_DEBUG_MROUTE) {
319 char group_str[INET_ADDRSTRLEN];
320 char source_str[INET_ADDRSTRLEN];
321 pim_inet4_dump("<group?>",
322 channel_oil->oil.mfcc_mcastgrp,
323 group_str, sizeof(group_str));
324 pim_inet4_dump("<source?>",
325 channel_oil->oil.mfcc_origin, source_str,
326 sizeof(source_str));
327 zlog_debug(
328 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
329 __FILE__, __PRETTY_FUNCTION__, oif->name,
330 pim_ifp->mroute_vif_index, source_str,
331 group_str);
332 }
333 return -1;
334 }
335
336 --channel_oil->oil_size;
337
338 if (PIM_DEBUG_MROUTE) {
339 char group_str[INET_ADDRSTRLEN];
340 char source_str[INET_ADDRSTRLEN];
341 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp,
342 group_str, sizeof(group_str));
343 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin,
344 source_str, sizeof(source_str));
345 zlog_debug(
346 "%s(%s): (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
347 __PRETTY_FUNCTION__, caller, source_str, group_str,
348 proto_mask, channel_oil->oil.mfcc_parent, oif->name,
349 pim_ifp->mroute_vif_index);
350 }
351
352 return 0;
353 }
354
355 void pim_channel_del_inherited_oif(struct channel_oil *c_oil,
356 struct interface *oif, const char *caller)
357 {
358 struct pim_upstream *up = c_oil->up;
359
360 pim_channel_del_oif(c_oil, oif, PIM_OIF_FLAG_PROTO_STAR,
361 caller);
362
363 /* if an inherited OIF is being removed join-desired can change
364 * if the inherited OIL is now empty and KAT is running
365 */
366 if (up && up->sg.src.s_addr != INADDR_ANY &&
367 pim_upstream_empty_inherited_olist(up))
368 pim_upstream_update_join_desired(up->pim, up);
369 }
370
371 static bool pim_channel_eval_oif_mute(struct channel_oil *c_oil,
372 struct pim_interface *pim_ifp)
373 {
374 struct pim_interface *pim_reg_ifp;
375 struct pim_interface *vxlan_ifp;
376 bool do_mute = false;
377 struct pim_instance *pim = c_oil->pim;
378
379 if (!c_oil->up)
380 return do_mute;
381
382 pim_reg_ifp = pim->regiface->info;
383 if (pim_ifp == pim_reg_ifp) {
384 /* suppress pimreg in the OIL if the mroute is not supposed to
385 * trigger register encapsulated data
386 */
387 if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil->up->flags))
388 do_mute = true;
389
390 return do_mute;
391 }
392
393 vxlan_ifp = pim_vxlan_get_term_ifp(pim);
394 if (pim_ifp == vxlan_ifp) {
395 /* 1. vxlan termination device must never be added to the
396 * origination mroute (and that can actually happen because
397 * of XG inheritance from the termination mroute) otherwise
398 * traffic will end up looping.
399 * PS: This check has also been extended to non-orig mroutes
400 * that have a local SIP as such mroutes can move back and
401 * forth between orig<=>non-orig type.
402 * 2. vxlan termination device should be removed from the non-DF
403 * to prevent duplicates to the overlay rxer
404 */
405 if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil->up->flags) ||
406 PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags) ||
407 pim_vxlan_is_local_sip(c_oil->up))
408 do_mute = true;
409
410 return do_mute;
411 }
412
413 return do_mute;
414 }
415
416 void pim_channel_update_oif_mute(struct channel_oil *c_oil,
417 struct pim_interface *pim_ifp)
418 {
419 bool old_mute;
420 bool new_mute;
421
422 /* If pim_ifp is not a part of the OIL there is nothing to do */
423 if (!c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])
424 return;
425
426 old_mute = !!(c_oil->oif_flags[pim_ifp->mroute_vif_index] &
427 PIM_OIF_FLAG_MUTE);
428 new_mute = pim_channel_eval_oif_mute(c_oil, pim_ifp);
429 if (old_mute == new_mute)
430 return;
431
432 if (new_mute)
433 c_oil->oif_flags[pim_ifp->mroute_vif_index] |=
434 PIM_OIF_FLAG_MUTE;
435 else
436 c_oil->oif_flags[pim_ifp->mroute_vif_index] &=
437 ~PIM_OIF_FLAG_MUTE;
438
439 pim_upstream_mroute_add(c_oil, __PRETTY_FUNCTION__);
440 }
441
442 /* pim_upstream has been set or cleared on the c_oil. re-eval mute state
443 * on all existing OIFs
444 */
445 static void pim_channel_update_mute(struct channel_oil *c_oil)
446 {
447 struct pim_interface *pim_reg_ifp;
448 struct pim_interface *vxlan_ifp;
449
450 pim_reg_ifp = c_oil->pim->regiface->info;
451 if (pim_reg_ifp)
452 pim_channel_update_oif_mute(c_oil, pim_reg_ifp);
453 vxlan_ifp = pim_vxlan_get_term_ifp(c_oil->pim);
454 if (vxlan_ifp)
455 pim_channel_update_oif_mute(c_oil, vxlan_ifp);
456 }
457
458 int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
459 uint32_t proto_mask, const char *caller)
460 {
461 struct pim_interface *pim_ifp;
462 int old_ttl;
463
464 /*
465 * If we've gotten here we've gone bad, but let's
466 * not take down pim
467 */
468 if (!channel_oil) {
469 zlog_warn("Attempt to Add OIF for non-existent channel oil");
470 return -1;
471 }
472
473 pim_ifp = oif->info;
474
475 /* Prevent single protocol from subscribing same interface to
476 channel (S,G) multiple times */
477 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
478 if (PIM_DEBUG_MROUTE) {
479 char group_str[INET_ADDRSTRLEN];
480 char source_str[INET_ADDRSTRLEN];
481 pim_inet4_dump("<group?>",
482 channel_oil->oil.mfcc_mcastgrp,
483 group_str, sizeof(group_str));
484 pim_inet4_dump("<source?>",
485 channel_oil->oil.mfcc_origin, source_str,
486 sizeof(source_str));
487 zlog_debug(
488 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
489 __FILE__, __PRETTY_FUNCTION__, proto_mask,
490 oif->name, pim_ifp->mroute_vif_index,
491 channel_oil->oil
492 .mfcc_ttls[pim_ifp->mroute_vif_index],
493 source_str, group_str);
494 }
495 return -3;
496 }
497
498 /* Allow other protocol to request subscription of same interface to
499 * channel (S,G), we need to note this information
500 */
501 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]
502 & PIM_OIF_FLAG_PROTO_ANY) {
503
504 /* Updating time here is not required as this time has to
505 * indicate when the interface is added
506 */
507
508 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
509 /* Check the OIF really exists before returning, and only log
510 warning otherwise */
511 if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
512 {
513 char group_str[INET_ADDRSTRLEN];
514 char source_str[INET_ADDRSTRLEN];
515 pim_inet4_dump("<group?>",
516 channel_oil->oil.mfcc_mcastgrp,
517 group_str, sizeof(group_str));
518 pim_inet4_dump("<source?>",
519 channel_oil->oil.mfcc_origin,
520 source_str, sizeof(source_str));
521 zlog_warn(
522 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
523 __FILE__, __PRETTY_FUNCTION__,
524 proto_mask, oif->name,
525 pim_ifp->mroute_vif_index,
526 channel_oil->oil.mfcc_ttls
527 [pim_ifp->mroute_vif_index],
528 source_str, group_str);
529 }
530 }
531
532 return 0;
533 }
534
535 old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index];
536
537 if (old_ttl > 0) {
538 if (PIM_DEBUG_MROUTE) {
539 char group_str[INET_ADDRSTRLEN];
540 char source_str[INET_ADDRSTRLEN];
541 pim_inet4_dump("<group?>",
542 channel_oil->oil.mfcc_mcastgrp,
543 group_str, sizeof(group_str));
544 pim_inet4_dump("<source?>",
545 channel_oil->oil.mfcc_origin, source_str,
546 sizeof(source_str));
547 zlog_debug(
548 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
549 __FILE__, __PRETTY_FUNCTION__, oif->name,
550 pim_ifp->mroute_vif_index, source_str,
551 group_str);
552 }
553 return -4;
554 }
555
556 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] =
557 PIM_MROUTE_MIN_TTL;
558
559 /* Some OIFs are held in a muted state i.e. the PIM state machine
560 * decided to include the OIF but additional status check such as
561 * MLAG DF role prevent it from being activated for traffic
562 * forwarding.
563 */
564 if (pim_channel_eval_oif_mute(channel_oil, pim_ifp))
565 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |=
566 PIM_OIF_FLAG_MUTE;
567 else
568 channel_oil->oif_flags[pim_ifp->mroute_vif_index] &=
569 ~PIM_OIF_FLAG_MUTE;
570
571 /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not
572 * valid to get installed in kernel.
573 */
574 if (channel_oil->oil.mfcc_parent != MAXVIFS) {
575 if (pim_upstream_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
576 if (PIM_DEBUG_MROUTE) {
577 char group_str[INET_ADDRSTRLEN];
578 char source_str[INET_ADDRSTRLEN];
579 pim_inet4_dump("<group?>",
580 channel_oil->oil.mfcc_mcastgrp,
581 group_str, sizeof(group_str));
582 pim_inet4_dump("<source?>",
583 channel_oil->oil.mfcc_origin, source_str,
584 sizeof(source_str));
585 zlog_debug(
586 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
587 __FILE__, __PRETTY_FUNCTION__, oif->name,
588 pim_ifp->mroute_vif_index, source_str,
589 group_str);
590 }
591
592 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index]
593 = old_ttl;
594 return -5;
595 }
596 }
597
598 channel_oil->oif_creation[pim_ifp->mroute_vif_index] =
599 pim_time_monotonic_sec();
600 ++channel_oil->oil_size;
601 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
602
603 if (PIM_DEBUG_MROUTE) {
604 char group_str[INET_ADDRSTRLEN];
605 char source_str[INET_ADDRSTRLEN];
606 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp,
607 group_str, sizeof(group_str));
608 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin,
609 source_str, sizeof(source_str));
610 zlog_debug(
611 "%s(%s): (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
612 __PRETTY_FUNCTION__, caller, source_str, group_str,
613 proto_mask, oif->name, pim_ifp->mroute_vif_index);
614 }
615
616 return 0;
617 }
618
619 int pim_channel_oil_empty(struct channel_oil *c_oil)
620 {
621 static struct mfcctl null_oil;
622
623 if (!c_oil)
624 return 1;
625
626 /* exclude pimreg from the OIL when checking if the inherited_oil is
627 * non-NULL.
628 * pimreg device (in all vrfs) uses a vifi of
629 * 0 (PIM_OIF_PIM_REGISTER_VIF) so we simply mfcc_ttls[0] */
630 return !memcmp(&c_oil->oil.mfcc_ttls[1], &null_oil.mfcc_ttls[1],
631 sizeof(null_oil.mfcc_ttls) - sizeof(null_oil.mfcc_ttls[0]));
632 }