1 /* PIM support for VxLAN BUM flooding
3 * Copyright (C) 2019 Cumulus Networks, Inc.
5 * This file is part of FRR.
7 * FRR is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2, or (at your option) any
12 * FRR is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
31 #include "pim_iface.h"
32 #include "pim_memory.h"
34 #include "pim_register.h"
36 #include "pim_upstream.h"
37 #include "pim_ifchannel.h"
39 #include "pim_zebra.h"
40 #include "pim_vxlan.h"
42 /* pim-vxlan global info */
43 struct pim_vxlan vxlan_info
, *pim_vxlan_p
= &vxlan_info
;
45 static void pim_vxlan_work_timer_setup(bool start
);
47 /*************************** vxlan work list **********************************
48 * A work list is maintained for staggered generation of pim null register
49 * messages for vxlan SG entries that are in a reg_join state.
51 * A max of 500 NULL registers are generated at one shot. If paused reg
52 * generation continues on the next second and so on till all register
53 * messages have been sent out. And the process is restarted every 60s.
55 * purpose of this null register generation is to setup the SPT and maintain
56 * independent of the presence of overlay BUM traffic.
57 ****************************************************************************/
58 static void pim_vxlan_do_reg_work(void)
60 struct listnode
*listnode
;
62 struct pim_vxlan_sg
*vxlan_sg
;
67 if (sec_count
> PIM_VXLAN_NULL_REG_INTERVAL
) {
69 listnode
= vxlan_info
.next_work
?
70 vxlan_info
.next_work
:
71 vxlan_info
.work_list
->head
;
72 if (PIM_DEBUG_VXLAN
&& listnode
)
73 zlog_debug("vxlan SG work %s",
74 vxlan_info
.next_work
? "continues" : "starts");
76 listnode
= vxlan_info
.next_work
;
79 for (; listnode
; listnode
= listnode
->next
) {
80 vxlan_sg
= (struct pim_vxlan_sg
*)listnode
->data
;
81 if (vxlan_sg
->up
&& (vxlan_sg
->up
->reg_state
== PIM_REG_JOIN
)) {
83 zlog_debug("vxlan SG %s periodic NULL register",
85 pim_null_register_send(vxlan_sg
->up
);
89 if (work_cnt
> vxlan_info
.max_work_cnt
) {
90 vxlan_info
.next_work
= listnode
->next
;
92 zlog_debug("vxlan SG %d work items proc and pause",
100 zlog_debug("vxlan SG %d work items proc", work_cnt
);
102 vxlan_info
.next_work
= NULL
;
105 /* Staggered work related info is initialized when the first work comes
108 static void pim_vxlan_init_work(void)
110 if (vxlan_info
.flags
& PIM_VXLANF_WORK_INITED
)
113 vxlan_info
.max_work_cnt
= PIM_VXLAN_WORK_MAX
;
114 vxlan_info
.flags
|= PIM_VXLANF_WORK_INITED
;
115 vxlan_info
.work_list
= list_new();
116 pim_vxlan_work_timer_setup(TRUE
/* start */);
119 static void pim_vxlan_add_work(struct pim_vxlan_sg
*vxlan_sg
)
121 if (vxlan_sg
->flags
& PIM_VXLAN_SGF_DEL_IN_PROG
) {
123 zlog_debug("vxlan SG %s skip work list; del-in-prog",
128 pim_vxlan_init_work();
130 /* already a part of the work list */
131 if (vxlan_sg
->work_node
)
135 zlog_debug("vxlan SG %s work list add",
137 vxlan_sg
->work_node
= listnode_add(vxlan_info
.work_list
, vxlan_sg
);
138 /* XXX: adjust max_work_cnt if needed */
141 static void pim_vxlan_del_work(struct pim_vxlan_sg
*vxlan_sg
)
143 if (!vxlan_sg
->work_node
)
147 zlog_debug("vxlan SG %s work list del",
150 if (vxlan_sg
->work_node
== vxlan_info
.next_work
)
151 vxlan_info
.next_work
= vxlan_sg
->work_node
->next
;
153 list_delete_node(vxlan_info
.work_list
, vxlan_sg
->work_node
);
154 vxlan_sg
->work_node
= NULL
;
157 void pim_vxlan_update_sg_reg_state(struct pim_instance
*pim
,
158 struct pim_upstream
*up
, bool reg_join
)
160 struct pim_vxlan_sg
*vxlan_sg
;
162 vxlan_sg
= pim_vxlan_sg_find(pim
, &up
->sg
);
166 /* add the vxlan sg entry to a work list for periodic reg joins.
167 * the entry will stay in the list as long as the register state is
171 pim_vxlan_add_work(vxlan_sg
);
173 pim_vxlan_del_work(vxlan_sg
);
176 static int pim_vxlan_work_timer_cb(struct thread
*t
)
178 pim_vxlan_do_reg_work();
179 pim_vxlan_work_timer_setup(true /* start */);
183 /* global 1second timer used for periodic processing */
184 static void pim_vxlan_work_timer_setup(bool start
)
186 THREAD_OFF(vxlan_info
.work_timer
);
188 thread_add_timer(router
->master
, pim_vxlan_work_timer_cb
, NULL
,
189 PIM_VXLAN_WORK_TIME
, &vxlan_info
.work_timer
);
192 /**************************** vxlan origination mroutes ***********************
193 * For every (local-vtep-ip, bum-mcast-grp) registered by evpn an origination
194 * mroute is setup by pimd. The purpose of this mroute is to forward vxlan
195 * encapsulated BUM (broadcast, unknown-unicast and unknown-multicast packets
196 * over the underlay.)
198 * Sample mroute (single VTEP):
199 * (27.0.0.7, 239.1.1.100) Iif: lo Oifs: uplink-1
201 * Sample mroute (anycast VTEP):
202 * (36.0.0.9, 239.1.1.100) Iif: peerlink-3.4094\
203 * Oifs: peerlink-3.4094 uplink-1
204 ***************************************************************************/
205 static void pim_vxlan_orig_mr_up_del(struct pim_vxlan_sg
*vxlan_sg
)
207 struct pim_upstream
*up
= vxlan_sg
->up
;
213 zlog_debug("vxlan SG %s orig mroute-up del",
217 if (up
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG
) {
218 /* clear out all the vxlan properties */
219 up
->flags
&= ~(PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG
|
220 PIM_UPSTREAM_FLAG_MASK_STATIC_IIF
|
221 PIM_UPSTREAM_FLAG_MASK_DISABLE_KAT_EXPIRY
|
222 PIM_UPSTREAM_FLAG_MASK_FORCE_PIMREG
|
223 PIM_UPSTREAM_FLAG_MASK_NO_PIMREG_DATA
|
224 PIM_UPSTREAM_FLAG_MASK_ALLOW_IIF_IN_OIL
);
226 /* We bring things to a grinding halt by force expirying
227 * the kat. Doing this will also remove the reference we
228 * created as a "vxlan" source and delete the upstream entry
229 * if there are no other references.
231 if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
232 THREAD_OFF(up
->t_ka_timer
);
233 up
= pim_upstream_keep_alive_timer_proc(up
);
235 /* this is really unexpected as we force vxlan
236 * origination mroutes active sources but just in
239 up
= pim_upstream_del(vxlan_sg
->pim
, up
,
240 __PRETTY_FUNCTION__
);
242 /* if there are other references register the source
246 pim_rpf_update(vxlan_sg
->pim
, up
, NULL
, 1 /* is_new */);
250 static void pim_vxlan_orig_mr_up_iif_update(struct pim_vxlan_sg
*vxlan_sg
)
254 /* update MFC with the new IIF */
255 pim_upstream_fill_static_iif(vxlan_sg
->up
, vxlan_sg
->iif
);
256 vif_index
= pim_if_find_vifindex_by_ifindex(vxlan_sg
->pim
,
257 vxlan_sg
->iif
->ifindex
);
259 pim_scan_individual_oil(vxlan_sg
->up
->channel_oil
,
263 zlog_debug("vxlan SG %s orig mroute-up updated with iif %s vifi %d",
265 vxlan_sg
->iif
?vxlan_sg
->iif
->name
:"-", vif_index
);
269 /* For every VxLAN BUM multicast group we setup a SG-up that has the following
270 * "forced properties" -
271 * 1. Directly connected on a DR interface i.e. we must act as an FHR
272 * 2. We prime the pump i.e. no multicast data is needed to register this
273 * source with the FHR. To do that we send periodic null registers if
274 * the SG entry is in a register-join state. We also prevent expiry of
276 * 3. As this SG is setup without data there is no need to register encapsulate
277 * data traffic. This encapsulation is explicitly skipped for the following
279 * a) Many levels of encapsulation are needed creating MTU disc challenges.
280 * Overlay BUM is encapsulated in a vxlan/UDP/IP header and then
281 * encapsulated again in a pim-register header.
282 * b) On a vxlan-aa setup both switches rx a copy of each BUM packet. if
283 * they both reg encapsulated traffic the RP will accept the duplicates
284 * as there are no RPF checks for this encapsulated data.
285 * a), b) can be workarounded if needed, but there is really no need because
286 * of (2) i.e. the pump is primed without data.
288 static void pim_vxlan_orig_mr_up_add(struct pim_vxlan_sg
*vxlan_sg
)
290 struct pim_upstream
*up
;
300 zlog_debug("vxlan SG %s orig mroute-up add with iif %s",
302 vxlan_sg
->iif
?vxlan_sg
->iif
->name
:"-");
304 PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_ORIG(flags
);
305 /* pin the IIF to lo or peerlink-subinterface and disable NHT */
306 PIM_UPSTREAM_FLAG_SET_STATIC_IIF(flags
);
307 /* Fake traffic by setting SRC_STREAM and starting KAT */
308 /* We intentionally skip updating ref count for SRC_STREAM/FHR.
309 * Setting SRC_VXLAN should have already created a reference
310 * preventing the entry from being deleted
312 PIM_UPSTREAM_FLAG_SET_FHR(flags
);
313 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(flags
);
314 /* Force pimreg even if non-DR. This is needed on a MLAG setup for
317 PIM_UPSTREAM_FLAG_SET_FORCE_PIMREG(flags
);
318 /* prevent KAT expiry. we want the MDT setup even if there is no BUM
321 PIM_UPSTREAM_FLAG_SET_DISABLE_KAT_EXPIRY(flags
);
322 /* SPT for vxlan BUM groups is primed and maintained via NULL
323 * registers so there is no need to reg-encapsulate
324 * vxlan-encapsulated overlay data traffic
326 PIM_UPSTREAM_FLAG_SET_NO_PIMREG_DATA(flags
);
327 /* On a MLAG setup we force a copy to the MLAG peer while also
328 * accepting traffic from the peer. To do this we set peerlink-rif as
329 * the IIF and also add it to the OIL
331 PIM_UPSTREAM_FLAG_SET_ALLOW_IIF_IN_OIL(flags
);
333 /* XXX: todo: defer pim_upstream add if pim is not enabled on the iif */
334 up
= pim_upstream_find(vxlan_sg
->pim
, &vxlan_sg
->sg
);
336 /* if the iif is set to something other than the vxlan_sg->iif
337 * we must dereg the old nexthop and force to new "static"
340 if (!PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up
->flags
)) {
341 nht_p
.family
= AF_INET
;
342 nht_p
.prefixlen
= IPV4_MAX_BITLEN
;
343 nht_p
.u
.prefix4
= up
->upstream_addr
;
344 pim_delete_tracked_nexthop(vxlan_sg
->pim
,
347 pim_upstream_ref(up
, flags
, __PRETTY_FUNCTION__
);
349 pim_vxlan_orig_mr_up_iif_update(vxlan_sg
);
351 up
= pim_upstream_add(vxlan_sg
->pim
, &vxlan_sg
->sg
,
352 vxlan_sg
->iif
, flags
,
353 __PRETTY_FUNCTION__
, NULL
);
359 zlog_debug("vxlan SG %s orig mroute-up add failed",
364 pim_upstream_keep_alive_timer_start(up
, vxlan_sg
->pim
->keep_alive_time
);
366 /* register the source with the RP */
367 if (up
->reg_state
== PIM_REG_NOINFO
) {
368 pim_register_join(up
);
369 pim_null_register_send(up
);
372 /* update the inherited OIL */
373 pim_upstream_inherited_olist(vxlan_sg
->pim
, up
);
376 static void pim_vxlan_orig_mr_oif_add(struct pim_vxlan_sg
*vxlan_sg
)
378 if (!vxlan_sg
->up
|| !vxlan_sg
->orig_oif
)
382 zlog_debug("vxlan SG %s oif %s add",
383 vxlan_sg
->sg_str
, vxlan_sg
->orig_oif
->name
);
385 vxlan_sg
->flags
|= PIM_VXLAN_SGF_OIF_INSTALLED
;
386 pim_channel_add_oif(vxlan_sg
->up
->channel_oil
,
387 vxlan_sg
->orig_oif
, PIM_OIF_FLAG_PROTO_VXLAN
);
390 static void pim_vxlan_orig_mr_oif_del(struct pim_vxlan_sg
*vxlan_sg
)
392 struct interface
*orig_oif
;
394 orig_oif
= vxlan_sg
->orig_oif
;
395 vxlan_sg
->orig_oif
= NULL
;
397 if (!(vxlan_sg
->flags
& PIM_VXLAN_SGF_OIF_INSTALLED
))
401 zlog_debug("vxlan SG %s oif %s del",
402 vxlan_sg
->sg_str
, orig_oif
->name
);
404 vxlan_sg
->flags
&= ~PIM_VXLAN_SGF_OIF_INSTALLED
;
405 pim_channel_del_oif(vxlan_sg
->up
->channel_oil
,
406 orig_oif
, PIM_OIF_FLAG_PROTO_VXLAN
);
409 static inline struct interface
*pim_vxlan_orig_mr_oif_get(
410 struct pim_instance
*pim
)
412 return (vxlan_mlag
.flags
& PIM_VXLAN_MLAGF_ENABLED
) ?
413 pim
->vxlan
.peerlink_rif
: NULL
;
416 /* Single VTEPs: IIF for the vxlan-origination-mroutes is lo or vrf-dev (if
417 * the mroute is in a non-default vrf).
418 * Anycast VTEPs: IIF is the MLAG ISL/peerlink.
420 static inline struct interface
*pim_vxlan_orig_mr_iif_get(
421 struct pim_instance
*pim
)
423 return ((vxlan_mlag
.flags
& PIM_VXLAN_MLAGF_ENABLED
) &&
424 pim
->vxlan
.peerlink_rif
) ?
425 pim
->vxlan
.peerlink_rif
: pim
->vxlan
.default_iif
;
428 static bool pim_vxlan_orig_mr_add_is_ok(struct pim_vxlan_sg
*vxlan_sg
)
430 struct pim_interface
*pim_ifp
;
432 vxlan_sg
->iif
= pim_vxlan_orig_mr_iif_get(vxlan_sg
->pim
);
436 pim_ifp
= (struct pim_interface
*)vxlan_sg
->iif
->info
;
437 if (!pim_ifp
|| (pim_ifp
->mroute_vif_index
< 0))
443 static void pim_vxlan_orig_mr_install(struct pim_vxlan_sg
*vxlan_sg
)
445 pim_vxlan_orig_mr_up_add(vxlan_sg
);
447 vxlan_sg
->orig_oif
= pim_vxlan_orig_mr_oif_get(vxlan_sg
->pim
);
448 pim_vxlan_orig_mr_oif_add(vxlan_sg
);
451 static void pim_vxlan_orig_mr_add(struct pim_vxlan_sg
*vxlan_sg
)
453 if (!pim_vxlan_orig_mr_add_is_ok(vxlan_sg
))
457 zlog_debug("vxlan SG %s orig-mr add", vxlan_sg
->sg_str
);
459 pim_vxlan_orig_mr_install(vxlan_sg
);
462 static void pim_vxlan_orig_mr_del(struct pim_vxlan_sg
*vxlan_sg
)
465 zlog_debug("vxlan SG %s orig-mr del", vxlan_sg
->sg_str
);
467 pim_vxlan_orig_mr_oif_del(vxlan_sg
);
468 pim_vxlan_orig_mr_up_del(vxlan_sg
);
471 /**************************** vxlan termination mroutes ***********************
472 * For every bum-mcast-grp registered by evpn a *G termination
473 * mroute is setup by pimd. The purpose of this mroute is to pull down vxlan
474 * packets with the bum-mcast-grp dip from the underlay and terminate the
475 * tunnel. This is done by including the vxlan termination device (ipmr-lo) in
476 * its OIL. The vxlan de-capsulated packets are subject to subsequent overlay
480 * (0.0.0.0, 239.1.1.100) Iif: uplink-1 Oifs: ipmr-lo, uplink-1
481 *****************************************************************************/
482 struct pim_interface
*pim_vxlan_get_term_ifp(struct pim_instance
*pim
)
484 return pim
->vxlan
.term_if
?
485 (struct pim_interface
*)pim
->vxlan
.term_if
->info
: NULL
;
488 static void pim_vxlan_term_mr_oif_add(struct pim_vxlan_sg
*vxlan_sg
)
490 if (vxlan_sg
->flags
& PIM_VXLAN_SGF_OIF_INSTALLED
)
494 zlog_debug("vxlan SG %s term-oif %s add",
495 vxlan_sg
->sg_str
, vxlan_sg
->term_oif
->name
);
497 if (pim_ifchannel_local_membership_add(vxlan_sg
->term_oif
,
499 vxlan_sg
->flags
|= PIM_VXLAN_SGF_OIF_INSTALLED
;
501 zlog_warn("vxlan SG %s term-oif %s add failed",
502 vxlan_sg
->sg_str
, vxlan_sg
->term_oif
->name
);
506 static void pim_vxlan_term_mr_oif_del(struct pim_vxlan_sg
*vxlan_sg
)
508 if (!(vxlan_sg
->flags
& PIM_VXLAN_SGF_OIF_INSTALLED
))
512 zlog_debug("vxlan SG %s oif %s del",
513 vxlan_sg
->sg_str
, vxlan_sg
->term_oif
->name
);
515 vxlan_sg
->flags
&= ~PIM_VXLAN_SGF_OIF_INSTALLED
;
516 pim_ifchannel_local_membership_del(vxlan_sg
->term_oif
, &vxlan_sg
->sg
);
519 static void pim_vxlan_term_mr_up_add(struct pim_vxlan_sg
*vxlan_sg
)
521 struct pim_upstream
*up
;
530 zlog_debug("vxlan SG %s term mroute-up add",
533 PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_TERM(flags
);
534 /* enable MLAG designated-forwarder election on termination mroutes */
535 PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(flags
);
537 up
= pim_upstream_add(vxlan_sg
->pim
, &vxlan_sg
->sg
,
538 NULL
/* iif */, flags
,
539 __PRETTY_FUNCTION__
, NULL
);
543 zlog_warn("vxlan SG %s term mroute-up add failed",
548 static void pim_vxlan_term_mr_up_del(struct pim_vxlan_sg
*vxlan_sg
)
550 struct pim_upstream
*up
= vxlan_sg
->up
;
556 zlog_debug("vxlan SG %s term mroute-up del",
559 if (up
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM
) {
560 /* clear out all the vxlan related flags */
561 up
->flags
&= ~(PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM
|
562 PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN
);
564 pim_upstream_del(vxlan_sg
->pim
, up
,
565 __PRETTY_FUNCTION__
);
569 static void pim_vxlan_term_mr_add(struct pim_vxlan_sg
*vxlan_sg
)
572 zlog_debug("vxlan SG %s term mroute add", vxlan_sg
->sg_str
);
574 vxlan_sg
->term_oif
= vxlan_sg
->pim
->vxlan
.term_if
;
575 if (!vxlan_sg
->term_oif
)
576 /* defer termination mroute till we have a termination device */
579 pim_vxlan_term_mr_up_add(vxlan_sg
);
580 /* set up local membership for the term-oif */
581 pim_vxlan_term_mr_oif_add(vxlan_sg
);
584 static void pim_vxlan_term_mr_del(struct pim_vxlan_sg
*vxlan_sg
)
587 zlog_debug("vxlan SG %s term mroute del", vxlan_sg
->sg_str
);
589 /* remove local membership associated with the term oif */
590 pim_vxlan_term_mr_oif_del(vxlan_sg
);
591 /* remove references to the upstream entry */
592 pim_vxlan_term_mr_up_del(vxlan_sg
);
595 /************************** vxlan SG cache management ************************/
596 static unsigned int pim_vxlan_sg_hash_key_make(void *p
)
598 struct pim_vxlan_sg
*vxlan_sg
= p
;
600 return (jhash_2words(vxlan_sg
->sg
.src
.s_addr
,
601 vxlan_sg
->sg
.grp
.s_addr
, 0));
604 static bool pim_vxlan_sg_hash_eq(const void *p1
, const void *p2
)
606 const struct pim_vxlan_sg
*sg1
= p1
;
607 const struct pim_vxlan_sg
*sg2
= p2
;
609 return ((sg1
->sg
.src
.s_addr
== sg2
->sg
.src
.s_addr
)
610 && (sg1
->sg
.grp
.s_addr
== sg2
->sg
.grp
.s_addr
));
613 static struct pim_vxlan_sg
*pim_vxlan_sg_new(struct pim_instance
*pim
,
614 struct prefix_sg
*sg
)
616 struct pim_vxlan_sg
*vxlan_sg
;
618 vxlan_sg
= XCALLOC(MTYPE_PIM_VXLAN_SG
, sizeof(*vxlan_sg
));
622 pim_str_sg_set(sg
, vxlan_sg
->sg_str
);
625 zlog_debug("vxlan SG %s alloc", vxlan_sg
->sg_str
);
627 vxlan_sg
= hash_get(pim
->vxlan
.sg_hash
, vxlan_sg
, hash_alloc_intern
);
632 struct pim_vxlan_sg
*pim_vxlan_sg_find(struct pim_instance
*pim
,
633 struct prefix_sg
*sg
)
635 struct pim_vxlan_sg lookup
;
638 return hash_lookup(pim
->vxlan
.sg_hash
, &lookup
);
641 struct pim_vxlan_sg
*pim_vxlan_sg_add(struct pim_instance
*pim
,
642 struct prefix_sg
*sg
)
644 struct pim_vxlan_sg
*vxlan_sg
;
646 vxlan_sg
= pim_vxlan_sg_find(pim
, sg
);
650 vxlan_sg
= pim_vxlan_sg_new(pim
, sg
);
652 if (pim_vxlan_is_orig_mroute(vxlan_sg
))
653 pim_vxlan_orig_mr_add(vxlan_sg
);
655 pim_vxlan_term_mr_add(vxlan_sg
);
660 void pim_vxlan_sg_del(struct pim_instance
*pim
, struct prefix_sg
*sg
)
662 struct pim_vxlan_sg
*vxlan_sg
;
664 vxlan_sg
= pim_vxlan_sg_find(pim
, sg
);
668 vxlan_sg
->flags
|= PIM_VXLAN_SGF_DEL_IN_PROG
;
670 pim_vxlan_del_work(vxlan_sg
);
672 if (pim_vxlan_is_orig_mroute(vxlan_sg
))
673 pim_vxlan_orig_mr_del(vxlan_sg
);
675 pim_vxlan_term_mr_del(vxlan_sg
);
677 hash_release(vxlan_sg
->pim
->vxlan
.sg_hash
, vxlan_sg
);
680 zlog_debug("vxlan SG %s free", vxlan_sg
->sg_str
);
682 XFREE(MTYPE_PIM_VXLAN_SG
, vxlan_sg
);
685 void pim_vxlan_init(struct pim_instance
*pim
)
689 snprintf(hash_name
, sizeof(hash_name
),
690 "PIM %s vxlan SG hash", pim
->vrf
->name
);
691 pim
->vxlan
.sg_hash
= hash_create(pim_vxlan_sg_hash_key_make
,
692 pim_vxlan_sg_hash_eq
, hash_name
);
695 void pim_vxlan_exit(struct pim_instance
*pim
)
697 if (pim
->vxlan
.sg_hash
) {
698 hash_clean(pim
->vxlan
.sg_hash
, NULL
);
699 hash_free(pim
->vxlan
.sg_hash
);
700 pim
->vxlan
.sg_hash
= NULL
;