]>
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"
34 #include "pim_vxlan.h"
36 // struct list *pim_channel_oil_list = NULL;
37 // struct hash *pim_channel_oil_hash = NULL;
39 static void pim_channel_update_mute(struct channel_oil
*c_oil
);
41 char *pim_channel_oil_dump(struct channel_oil
*c_oil
, char *buf
, size_t size
)
44 struct interface
*ifp
;
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
: "(?)");
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
: "(?)");
67 static int pim_channel_oil_compare(struct channel_oil
*c1
,
68 struct channel_oil
*c2
)
70 if (ntohl(c1
->oil
.mfcc_mcastgrp
.s_addr
)
71 < ntohl(c2
->oil
.mfcc_mcastgrp
.s_addr
))
74 if (ntohl(c1
->oil
.mfcc_mcastgrp
.s_addr
)
75 > ntohl(c2
->oil
.mfcc_mcastgrp
.s_addr
))
78 if (ntohl(c1
->oil
.mfcc_origin
.s_addr
)
79 < ntohl(c2
->oil
.mfcc_origin
.s_addr
))
82 if (ntohl(c1
->oil
.mfcc_origin
.s_addr
)
83 > ntohl(c2
->oil
.mfcc_origin
.s_addr
))
89 static bool pim_oil_equal(const void *arg1
, const void *arg2
)
91 const struct channel_oil
*c1
= (const struct channel_oil
*)arg1
;
92 const struct channel_oil
*c2
= (const struct channel_oil
*)arg2
;
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
))
101 static unsigned int pim_oil_hash_key(const void *arg
)
103 const struct channel_oil
*oil
= arg
;
105 return jhash_2words(oil
->oil
.mfcc_mcastgrp
.s_addr
,
106 oil
->oil
.mfcc_origin
.s_addr
, 0);
109 void pim_oil_init(struct pim_instance
*pim
)
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
);
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
;
123 void pim_oil_terminate(struct pim_instance
*pim
)
125 if (pim
->channel_oil_list
)
126 list_delete(&pim
->channel_oil_list
);
128 if (pim
->channel_oil_hash
)
129 hash_free(pim
->channel_oil_hash
);
130 pim
->channel_oil_hash
= NULL
;
133 void pim_channel_oil_free(struct channel_oil
*c_oil
)
135 XFREE(MTYPE_PIM_CHANNEL_OIL
, c_oil
);
138 struct channel_oil
*pim_find_channel_oil(struct pim_instance
*pim
,
139 struct prefix_sg
*sg
)
141 struct channel_oil
*c_oil
= NULL
;
142 struct channel_oil lookup
;
144 lookup
.oil
.mfcc_mcastgrp
= sg
->grp
;
145 lookup
.oil
.mfcc_origin
= sg
->src
;
147 c_oil
= hash_lookup(pim
->channel_oil_hash
, &lookup
);
152 struct channel_oil
*pim_channel_oil_add(struct pim_instance
*pim
,
153 struct prefix_sg
*sg
,
156 struct channel_oil
*c_oil
;
158 c_oil
= pim_find_channel_oil(pim
, sg
);
160 ++c_oil
->oil_ref_count
;
163 /* channel might be present prior to upstream */
164 c_oil
->up
= pim_upstream_find(
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
170 pim_channel_update_mute(c_oil
);
173 /* check if the IIF has changed
174 * XXX - is this really needed
176 pim_upstream_mroute_iif_update(c_oil
, __func__
);
178 if (PIM_DEBUG_MROUTE
)
180 "%s(%s): Existing oil for %pSG4 Ref Count: %d (Post Increment)",
181 __PRETTY_FUNCTION__
, name
, sg
,
182 c_oil
->oil_ref_count
);
186 c_oil
= XCALLOC(MTYPE_PIM_CHANNEL_OIL
, sizeof(*c_oil
));
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
);
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
);
198 listnode_add_sort(pim
->channel_oil_list
, c_oil
);
200 if (PIM_DEBUG_MROUTE
)
201 zlog_debug("%s(%s): c_oil %s add",
202 __func__
, name
, pim_str_sg_dump(sg
));
207 struct channel_oil
*pim_channel_oil_del(struct channel_oil
*c_oil
,
210 if (PIM_DEBUG_MROUTE
) {
211 struct prefix_sg sg
= {.src
= c_oil
->oil
.mfcc_mcastgrp
,
212 .grp
= c_oil
->oil
.mfcc_origin
};
215 "%s(%s): Del oil for %pSG4, Ref Count: %d (Predecrement)",
216 __PRETTY_FUNCTION__
, name
, &sg
, c_oil
->oil_ref_count
);
218 --c_oil
->oil_ref_count
;
220 if (c_oil
->oil_ref_count
< 1) {
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()
227 listnode_delete(c_oil
->pim
->channel_oil_list
, c_oil
);
228 hash_release(c_oil
->pim
->channel_oil_hash
, c_oil
);
230 pim_channel_oil_free(c_oil
);
237 void pim_channel_oil_upstream_deref(struct channel_oil
*c_oil
)
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.
243 c_oil
= pim_channel_oil_del(c_oil
, __func__
);
245 /* note: here we assume that c_oil->up has already been
248 pim_channel_update_mute(c_oil
);
252 int pim_channel_del_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
253 uint32_t proto_mask
, const char *caller
)
255 struct pim_interface
*pim_ifp
;
257 zassert(channel_oil
);
263 * Don't do anything if we've been asked to remove a source
264 * that is not actually on it.
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
,
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
,
280 ->oif_flags
[pim_ifp
->mroute_vif_index
],
281 oif
->name
, pim_ifp
->mroute_vif_index
,
283 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
284 source_str
, group_str
);
289 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~proto_mask
;
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
,
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
,
307 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
308 source_str
, group_str
);
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
;
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
,
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
,
336 --channel_oil
->oil_size
;
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
));
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
);
355 void pim_channel_del_inherited_oif(struct channel_oil
*c_oil
,
356 struct interface
*oif
, const char *caller
)
358 struct pim_upstream
*up
= c_oil
->up
;
360 pim_channel_del_oif(c_oil
, oif
, PIM_OIF_FLAG_PROTO_STAR
,
363 /* if an inherited OIF is being removed join-desired can change
364 * if the inherited OIL is now empty and KAT is running
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
);
371 static bool pim_channel_eval_oif_mute(struct channel_oil
*c_oil
,
372 struct pim_interface
*pim_ifp
)
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
;
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
387 if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil
->up
->flags
))
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
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
))
416 void pim_channel_update_oif_mute(struct channel_oil
*c_oil
,
417 struct pim_interface
*pim_ifp
)
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
])
426 old_mute
= !!(c_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &
428 new_mute
= pim_channel_eval_oif_mute(c_oil
, pim_ifp
);
429 if (old_mute
== new_mute
)
433 c_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |=
436 c_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &=
439 pim_upstream_mroute_add(c_oil
, __PRETTY_FUNCTION__
);
442 /* pim_upstream has been set or cleared on the c_oil. re-eval mute state
443 * on all existing OIFs
445 static void pim_channel_update_mute(struct channel_oil
*c_oil
)
447 struct pim_interface
*pim_reg_ifp
;
448 struct pim_interface
*vxlan_ifp
;
450 pim_reg_ifp
= c_oil
->pim
->regiface
->info
;
452 pim_channel_update_oif_mute(c_oil
, pim_reg_ifp
);
453 vxlan_ifp
= pim_vxlan_get_term_ifp(c_oil
->pim
);
455 pim_channel_update_oif_mute(c_oil
, vxlan_ifp
);
458 int pim_channel_add_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
459 uint32_t proto_mask
, const char *caller
)
461 struct pim_interface
*pim_ifp
;
465 * If we've gotten here we've gone bad, but let's
469 zlog_warn("Attempt to Add OIF for non-existent channel oil");
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
,
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
,
492 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
493 source_str
, group_str
);
498 /* Allow other protocol to request subscription of same interface to
499 * channel (S,G), we need to note this information
501 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]
502 & PIM_OIF_FLAG_PROTO_ANY
) {
504 /* Updating time here is not required as this time has to
505 * indicate when the interface is added
508 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
509 /* Check the OIF really exists before returning, and only log
511 if (channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] < 1) {
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
));
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
);
535 old_ttl
= channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
];
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
,
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
,
556 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] =
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
564 if (pim_channel_eval_oif_mute(channel_oil
, pim_ifp
))
565 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |=
568 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &=
571 /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not
572 * valid to get installed in kernel.
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
,
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
,
592 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
]
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
;
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
));
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
);
619 int pim_channel_oil_empty(struct channel_oil
*c_oil
)
621 static struct mfcctl null_oil
;
626 /* exclude pimreg from the OIL when checking if the inherited_oil is
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]));