]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_vxlan.c
pimd: add termination mroutes for each vxlan multicast tunnels
[mirror_frr.git] / pimd / pim_vxlan.c
1 /* PIM support for VxLAN BUM flooding
2 *
3 * Copyright (C) 2019 Cumulus Networks, Inc.
4 *
5 * This file is part of FRR.
6 *
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
10 * later version.
11 *
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.
20 */
21
22 #include <zebra.h>
23
24 #include <hash.h>
25 #include <jhash.h>
26 #include <log.h>
27 #include <prefix.h>
28 #include <vrf.h>
29
30 #include "pimd.h"
31 #include "pim_iface.h"
32 #include "pim_memory.h"
33 #include "pim_oil.h"
34 #include "pim_register.h"
35 #include "pim_str.h"
36 #include "pim_upstream.h"
37 #include "pim_ifchannel.h"
38 #include "pim_nht.h"
39 #include "pim_zebra.h"
40 #include "pim_vxlan.h"
41
42 /* pim-vxlan global info */
43 struct pim_vxlan vxlan_info, *pim_vxlan_p = &vxlan_info;
44
45 static void pim_vxlan_work_timer_setup(bool start);
46
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.
50 *
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.
54 *
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)
59 {
60 struct listnode *listnode;
61 int work_cnt = 0;
62 struct pim_vxlan_sg *vxlan_sg;
63 static int sec_count;
64
65 ++sec_count;
66
67 if (sec_count > PIM_VXLAN_NULL_REG_INTERVAL) {
68 sec_count = 0;
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");
75 } else {
76 listnode = vxlan_info.next_work;
77 }
78
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)) {
82 if (PIM_DEBUG_VXLAN)
83 zlog_debug("vxlan SG %s periodic NULL register",
84 vxlan_sg->sg_str);
85 pim_null_register_send(vxlan_sg->up);
86 ++work_cnt;
87 }
88
89 if (work_cnt > vxlan_info.max_work_cnt) {
90 vxlan_info.next_work = listnode->next;
91 if (PIM_DEBUG_VXLAN)
92 zlog_debug("vxlan SG %d work items proc and pause",
93 work_cnt);
94 return;
95 }
96 }
97
98 if (work_cnt) {
99 if (PIM_DEBUG_VXLAN)
100 zlog_debug("vxlan SG %d work items proc", work_cnt);
101 }
102 vxlan_info.next_work = NULL;
103 }
104
105 /* Staggered work related info is initialized when the first work comes
106 * along
107 */
108 static void pim_vxlan_init_work(void)
109 {
110 if (vxlan_info.flags & PIM_VXLANF_WORK_INITED)
111 return;
112
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 */);
117 }
118
119 static void pim_vxlan_add_work(struct pim_vxlan_sg *vxlan_sg)
120 {
121 if (vxlan_sg->flags & PIM_VXLAN_SGF_DEL_IN_PROG) {
122 if (PIM_DEBUG_VXLAN)
123 zlog_debug("vxlan SG %s skip work list; del-in-prog",
124 vxlan_sg->sg_str);
125 return;
126 }
127
128 pim_vxlan_init_work();
129
130 /* already a part of the work list */
131 if (vxlan_sg->work_node)
132 return;
133
134 if (PIM_DEBUG_VXLAN)
135 zlog_debug("vxlan SG %s work list add",
136 vxlan_sg->sg_str);
137 vxlan_sg->work_node = listnode_add(vxlan_info.work_list, vxlan_sg);
138 /* XXX: adjust max_work_cnt if needed */
139 }
140
141 static void pim_vxlan_del_work(struct pim_vxlan_sg *vxlan_sg)
142 {
143 if (!vxlan_sg->work_node)
144 return;
145
146 if (PIM_DEBUG_VXLAN)
147 zlog_debug("vxlan SG %s work list del",
148 vxlan_sg->sg_str);
149
150 if (vxlan_sg->work_node == vxlan_info.next_work)
151 vxlan_info.next_work = vxlan_sg->work_node->next;
152
153 list_delete_node(vxlan_info.work_list, vxlan_sg->work_node);
154 vxlan_sg->work_node = NULL;
155 }
156
157 void pim_vxlan_update_sg_reg_state(struct pim_instance *pim,
158 struct pim_upstream *up, bool reg_join)
159 {
160 struct pim_vxlan_sg *vxlan_sg;
161
162 vxlan_sg = pim_vxlan_sg_find(pim, &up->sg);
163 if (!vxlan_sg)
164 return;
165
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
168 * PIM_REG_JOIN
169 */
170 if (reg_join)
171 pim_vxlan_add_work(vxlan_sg);
172 else
173 pim_vxlan_del_work(vxlan_sg);
174 }
175
176 static int pim_vxlan_work_timer_cb(struct thread *t)
177 {
178 pim_vxlan_do_reg_work();
179 pim_vxlan_work_timer_setup(true /* start */);
180 return 0;
181 }
182
183 /* global 1second timer used for periodic processing */
184 static void pim_vxlan_work_timer_setup(bool start)
185 {
186 THREAD_OFF(vxlan_info.work_timer);
187 if (start)
188 thread_add_timer(router->master, pim_vxlan_work_timer_cb, NULL,
189 PIM_VXLAN_WORK_TIME, &vxlan_info.work_timer);
190 }
191
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.)
197 *
198 * Sample mroute (single VTEP):
199 * (27.0.0.7, 239.1.1.100) Iif: lo Oifs: uplink-1
200 *
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)
206 {
207 struct pim_upstream *up = vxlan_sg->up;
208
209 if (!up)
210 return;
211
212 if (PIM_DEBUG_VXLAN)
213 zlog_debug("vxlan SG %s orig mroute-up del",
214 vxlan_sg->sg_str);
215
216 vxlan_sg->up = NULL;
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);
225
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.
230 */
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);
234 } else {
235 /* this is really unexpected as we force vxlan
236 * origination mroutes active sources but just in
237 * case
238 */
239 up = pim_upstream_del(vxlan_sg->pim, up,
240 __PRETTY_FUNCTION__);
241 }
242 /* if there are other references register the source
243 * for nht
244 */
245 if (up)
246 pim_rpf_update(vxlan_sg->pim, up, NULL, 1 /* is_new */);
247 }
248 }
249
250 static void pim_vxlan_orig_mr_up_iif_update(struct pim_vxlan_sg *vxlan_sg)
251 {
252 int vif_index;
253
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);
258 if (vif_index > 0)
259 pim_scan_individual_oil(vxlan_sg->up->channel_oil,
260 vif_index);
261
262 if (PIM_DEBUG_VXLAN)
263 zlog_debug("vxlan SG %s orig mroute-up updated with iif %s vifi %d",
264 vxlan_sg->sg_str,
265 vxlan_sg->iif?vxlan_sg->iif->name:"-", vif_index);
266
267 }
268
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
275 * KAT.
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
278 * reasons -
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.
287 */
288 static void pim_vxlan_orig_mr_up_add(struct pim_vxlan_sg *vxlan_sg)
289 {
290 struct pim_upstream *up;
291 int flags = 0;
292 struct prefix nht_p;
293
294 if (vxlan_sg->up) {
295 /* nothing to do */
296 return;
297 }
298
299 if (PIM_DEBUG_VXLAN)
300 zlog_debug("vxlan SG %s orig mroute-up add with iif %s",
301 vxlan_sg->sg_str,
302 vxlan_sg->iif?vxlan_sg->iif->name:"-");
303
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
311 */
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
315 * VxLAN AA
316 */
317 PIM_UPSTREAM_FLAG_SET_FORCE_PIMREG(flags);
318 /* prevent KAT expiry. we want the MDT setup even if there is no BUM
319 * traffic
320 */
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
325 */
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
330 */
331 PIM_UPSTREAM_FLAG_SET_ALLOW_IIF_IN_OIL(flags);
332
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);
335 if (up) {
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"
338 * iif
339 */
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,
345 &nht_p, up, NULL);
346 }
347 pim_upstream_ref(up, flags, __PRETTY_FUNCTION__);
348 vxlan_sg->up = up;
349 pim_vxlan_orig_mr_up_iif_update(vxlan_sg);
350 } else {
351 up = pim_upstream_add(vxlan_sg->pim, &vxlan_sg->sg,
352 vxlan_sg->iif, flags,
353 __PRETTY_FUNCTION__, NULL);
354 vxlan_sg->up = up;
355 }
356
357 if (!up) {
358 if (PIM_DEBUG_VXLAN)
359 zlog_debug("vxlan SG %s orig mroute-up add failed",
360 vxlan_sg->sg_str);
361 return;
362 }
363
364 pim_upstream_keep_alive_timer_start(up, vxlan_sg->pim->keep_alive_time);
365
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);
370 }
371
372 /* update the inherited OIL */
373 pim_upstream_inherited_olist(vxlan_sg->pim, up);
374 }
375
376 static void pim_vxlan_orig_mr_oif_add(struct pim_vxlan_sg *vxlan_sg)
377 {
378 if (!vxlan_sg->up || !vxlan_sg->orig_oif)
379 return;
380
381 if (PIM_DEBUG_VXLAN)
382 zlog_debug("vxlan SG %s oif %s add",
383 vxlan_sg->sg_str, vxlan_sg->orig_oif->name);
384
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);
388 }
389
390 static void pim_vxlan_orig_mr_oif_del(struct pim_vxlan_sg *vxlan_sg)
391 {
392 struct interface *orig_oif;
393
394 orig_oif = vxlan_sg->orig_oif;
395 vxlan_sg->orig_oif = NULL;
396
397 if (!(vxlan_sg->flags & PIM_VXLAN_SGF_OIF_INSTALLED))
398 return;
399
400 if (PIM_DEBUG_VXLAN)
401 zlog_debug("vxlan SG %s oif %s del",
402 vxlan_sg->sg_str, orig_oif->name);
403
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);
407 }
408
409 static inline struct interface *pim_vxlan_orig_mr_oif_get(
410 struct pim_instance *pim)
411 {
412 return (vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED) ?
413 pim->vxlan.peerlink_rif : NULL;
414 }
415
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.
419 */
420 static inline struct interface *pim_vxlan_orig_mr_iif_get(
421 struct pim_instance *pim)
422 {
423 return ((vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED) &&
424 pim->vxlan.peerlink_rif) ?
425 pim->vxlan.peerlink_rif : pim->vxlan.default_iif;
426 }
427
428 static bool pim_vxlan_orig_mr_add_is_ok(struct pim_vxlan_sg *vxlan_sg)
429 {
430 struct pim_interface *pim_ifp;
431
432 vxlan_sg->iif = pim_vxlan_orig_mr_iif_get(vxlan_sg->pim);
433 if (!vxlan_sg->iif)
434 return false;
435
436 pim_ifp = (struct pim_interface *)vxlan_sg->iif->info;
437 if (!pim_ifp || (pim_ifp->mroute_vif_index < 0))
438 return false;
439
440 return true;
441 }
442
443 static void pim_vxlan_orig_mr_install(struct pim_vxlan_sg *vxlan_sg)
444 {
445 pim_vxlan_orig_mr_up_add(vxlan_sg);
446
447 vxlan_sg->orig_oif = pim_vxlan_orig_mr_oif_get(vxlan_sg->pim);
448 pim_vxlan_orig_mr_oif_add(vxlan_sg);
449 }
450
451 static void pim_vxlan_orig_mr_add(struct pim_vxlan_sg *vxlan_sg)
452 {
453 if (!pim_vxlan_orig_mr_add_is_ok(vxlan_sg))
454 return;
455
456 if (PIM_DEBUG_VXLAN)
457 zlog_debug("vxlan SG %s orig-mr add", vxlan_sg->sg_str);
458
459 pim_vxlan_orig_mr_install(vxlan_sg);
460 }
461
462 static void pim_vxlan_orig_mr_del(struct pim_vxlan_sg *vxlan_sg)
463 {
464 if (PIM_DEBUG_VXLAN)
465 zlog_debug("vxlan SG %s orig-mr del", vxlan_sg->sg_str);
466
467 pim_vxlan_orig_mr_oif_del(vxlan_sg);
468 pim_vxlan_orig_mr_up_del(vxlan_sg);
469 }
470
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
477 * bridging.
478 *
479 * Sample mroute:
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)
483 {
484 return pim->vxlan.term_if ?
485 (struct pim_interface *)pim->vxlan.term_if->info : NULL;
486 }
487
488 static void pim_vxlan_term_mr_oif_add(struct pim_vxlan_sg *vxlan_sg)
489 {
490 if (vxlan_sg->flags & PIM_VXLAN_SGF_OIF_INSTALLED)
491 return;
492
493 if (PIM_DEBUG_VXLAN)
494 zlog_debug("vxlan SG %s term-oif %s add",
495 vxlan_sg->sg_str, vxlan_sg->term_oif->name);
496
497 if (pim_ifchannel_local_membership_add(vxlan_sg->term_oif,
498 &vxlan_sg->sg)) {
499 vxlan_sg->flags |= PIM_VXLAN_SGF_OIF_INSTALLED;
500 } else {
501 zlog_warn("vxlan SG %s term-oif %s add failed",
502 vxlan_sg->sg_str, vxlan_sg->term_oif->name);
503 }
504 }
505
506 static void pim_vxlan_term_mr_oif_del(struct pim_vxlan_sg *vxlan_sg)
507 {
508 if (!(vxlan_sg->flags & PIM_VXLAN_SGF_OIF_INSTALLED))
509 return;
510
511 if (PIM_DEBUG_VXLAN)
512 zlog_debug("vxlan SG %s oif %s del",
513 vxlan_sg->sg_str, vxlan_sg->term_oif->name);
514
515 vxlan_sg->flags &= ~PIM_VXLAN_SGF_OIF_INSTALLED;
516 pim_ifchannel_local_membership_del(vxlan_sg->term_oif, &vxlan_sg->sg);
517 }
518
519 static void pim_vxlan_term_mr_up_add(struct pim_vxlan_sg *vxlan_sg)
520 {
521 struct pim_upstream *up;
522 int flags = 0;
523
524 if (vxlan_sg->up) {
525 /* nothing to do */
526 return;
527 }
528
529 if (PIM_DEBUG_VXLAN)
530 zlog_debug("vxlan SG %s term mroute-up add",
531 vxlan_sg->sg_str);
532
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);
536
537 up = pim_upstream_add(vxlan_sg->pim, &vxlan_sg->sg,
538 NULL /* iif */, flags,
539 __PRETTY_FUNCTION__, NULL);
540 vxlan_sg->up = up;
541
542 if (!up) {
543 zlog_warn("vxlan SG %s term mroute-up add failed",
544 vxlan_sg->sg_str);
545 }
546 }
547
548 static void pim_vxlan_term_mr_up_del(struct pim_vxlan_sg *vxlan_sg)
549 {
550 struct pim_upstream *up = vxlan_sg->up;
551
552 if (!up)
553 return;
554
555 if (PIM_DEBUG_VXLAN)
556 zlog_debug("vxlan SG %s term mroute-up del",
557 vxlan_sg->sg_str);
558 vxlan_sg->up = NULL;
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);
563
564 pim_upstream_del(vxlan_sg->pim, up,
565 __PRETTY_FUNCTION__);
566 }
567 }
568
569 static void pim_vxlan_term_mr_add(struct pim_vxlan_sg *vxlan_sg)
570 {
571 if (PIM_DEBUG_VXLAN)
572 zlog_debug("vxlan SG %s term mroute add", vxlan_sg->sg_str);
573
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 */
577 return;
578
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);
582 }
583
584 static void pim_vxlan_term_mr_del(struct pim_vxlan_sg *vxlan_sg)
585 {
586 if (PIM_DEBUG_VXLAN)
587 zlog_debug("vxlan SG %s term mroute del", vxlan_sg->sg_str);
588
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);
593 }
594
595 /************************** vxlan SG cache management ************************/
596 static unsigned int pim_vxlan_sg_hash_key_make(void *p)
597 {
598 struct pim_vxlan_sg *vxlan_sg = p;
599
600 return (jhash_2words(vxlan_sg->sg.src.s_addr,
601 vxlan_sg->sg.grp.s_addr, 0));
602 }
603
604 static bool pim_vxlan_sg_hash_eq(const void *p1, const void *p2)
605 {
606 const struct pim_vxlan_sg *sg1 = p1;
607 const struct pim_vxlan_sg *sg2 = p2;
608
609 return ((sg1->sg.src.s_addr == sg2->sg.src.s_addr)
610 && (sg1->sg.grp.s_addr == sg2->sg.grp.s_addr));
611 }
612
613 static struct pim_vxlan_sg *pim_vxlan_sg_new(struct pim_instance *pim,
614 struct prefix_sg *sg)
615 {
616 struct pim_vxlan_sg *vxlan_sg;
617
618 vxlan_sg = XCALLOC(MTYPE_PIM_VXLAN_SG, sizeof(*vxlan_sg));
619
620 vxlan_sg->pim = pim;
621 vxlan_sg->sg = *sg;
622 pim_str_sg_set(sg, vxlan_sg->sg_str);
623
624 if (PIM_DEBUG_VXLAN)
625 zlog_debug("vxlan SG %s alloc", vxlan_sg->sg_str);
626
627 vxlan_sg = hash_get(pim->vxlan.sg_hash, vxlan_sg, hash_alloc_intern);
628
629 return vxlan_sg;
630 }
631
632 struct pim_vxlan_sg *pim_vxlan_sg_find(struct pim_instance *pim,
633 struct prefix_sg *sg)
634 {
635 struct pim_vxlan_sg lookup;
636
637 lookup.sg = *sg;
638 return hash_lookup(pim->vxlan.sg_hash, &lookup);
639 }
640
641 struct pim_vxlan_sg *pim_vxlan_sg_add(struct pim_instance *pim,
642 struct prefix_sg *sg)
643 {
644 struct pim_vxlan_sg *vxlan_sg;
645
646 vxlan_sg = pim_vxlan_sg_find(pim, sg);
647 if (vxlan_sg)
648 return vxlan_sg;
649
650 vxlan_sg = pim_vxlan_sg_new(pim, sg);
651
652 if (pim_vxlan_is_orig_mroute(vxlan_sg))
653 pim_vxlan_orig_mr_add(vxlan_sg);
654 else
655 pim_vxlan_term_mr_add(vxlan_sg);
656
657 return vxlan_sg;
658 }
659
660 void pim_vxlan_sg_del(struct pim_instance *pim, struct prefix_sg *sg)
661 {
662 struct pim_vxlan_sg *vxlan_sg;
663
664 vxlan_sg = pim_vxlan_sg_find(pim, sg);
665 if (!vxlan_sg)
666 return;
667
668 vxlan_sg->flags |= PIM_VXLAN_SGF_DEL_IN_PROG;
669
670 pim_vxlan_del_work(vxlan_sg);
671
672 if (pim_vxlan_is_orig_mroute(vxlan_sg))
673 pim_vxlan_orig_mr_del(vxlan_sg);
674 else
675 pim_vxlan_term_mr_del(vxlan_sg);
676
677 hash_release(vxlan_sg->pim->vxlan.sg_hash, vxlan_sg);
678
679 if (PIM_DEBUG_VXLAN)
680 zlog_debug("vxlan SG %s free", vxlan_sg->sg_str);
681
682 XFREE(MTYPE_PIM_VXLAN_SG, vxlan_sg);
683 }
684
685 void pim_vxlan_init(struct pim_instance *pim)
686 {
687 char hash_name[64];
688
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);
693 }
694
695 void pim_vxlan_exit(struct pim_instance *pim)
696 {
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;
701 }
702 }