Line data Source code
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 : #include "pim_mlag.h"
42 :
43 : /* pim-vxlan global info */
44 : struct pim_vxlan vxlan_info, *pim_vxlan_p = &vxlan_info;
45 :
46 : static void pim_vxlan_work_timer_setup(bool start);
47 : static void pim_vxlan_set_peerlink_rif(struct pim_instance *pim,
48 : struct interface *ifp);
49 :
50 : /*************************** vxlan work list **********************************
51 : * A work list is maintained for staggered generation of pim null register
52 : * messages for vxlan SG entries that are in a reg_join state.
53 : *
54 : * A max of 500 NULL registers are generated at one shot. If paused reg
55 : * generation continues on the next second and so on till all register
56 : * messages have been sent out. And the process is restarted every 60s.
57 : *
58 : * purpose of this null register generation is to setup the SPT and maintain
59 : * independent of the presence of overlay BUM traffic.
60 : ****************************************************************************/
61 0 : static void pim_vxlan_do_reg_work(void)
62 : {
63 0 : struct listnode *listnode;
64 0 : int work_cnt = 0;
65 0 : struct pim_vxlan_sg *vxlan_sg;
66 0 : static int sec_count;
67 :
68 0 : ++sec_count;
69 :
70 0 : if (sec_count > PIM_VXLAN_NULL_REG_INTERVAL) {
71 0 : sec_count = 0;
72 0 : listnode = vxlan_info.next_work ?
73 0 : vxlan_info.next_work :
74 0 : vxlan_info.work_list->head;
75 0 : if (PIM_DEBUG_VXLAN && listnode)
76 0 : zlog_debug("vxlan SG work %s",
77 : vxlan_info.next_work ? "continues" : "starts");
78 : } else {
79 0 : listnode = vxlan_info.next_work;
80 : }
81 :
82 0 : for (; listnode; listnode = listnode->next) {
83 0 : vxlan_sg = (struct pim_vxlan_sg *)listnode->data;
84 0 : if (vxlan_sg->up && (vxlan_sg->up->reg_state == PIM_REG_JOIN)) {
85 0 : if (PIM_DEBUG_VXLAN)
86 0 : zlog_debug("vxlan SG %s periodic NULL register",
87 : vxlan_sg->sg_str);
88 :
89 : /*
90 : * If we are on the work queue *and* the rpf
91 : * has been lost on the vxlan_sg->up let's
92 : * make sure that we don't send it.
93 : */
94 0 : if (vxlan_sg->up->rpf.source_nexthop.interface) {
95 0 : pim_null_register_send(vxlan_sg->up);
96 0 : ++work_cnt;
97 : }
98 : }
99 :
100 0 : if (work_cnt > vxlan_info.max_work_cnt) {
101 0 : vxlan_info.next_work = listnode->next;
102 0 : if (PIM_DEBUG_VXLAN)
103 0 : zlog_debug("vxlan SG %d work items proc and pause",
104 : work_cnt);
105 0 : return;
106 : }
107 : }
108 :
109 0 : if (work_cnt) {
110 0 : if (PIM_DEBUG_VXLAN)
111 0 : zlog_debug("vxlan SG %d work items proc", work_cnt);
112 : }
113 0 : vxlan_info.next_work = NULL;
114 : }
115 :
116 : /* Staggered work related info is initialized when the first work comes
117 : * along
118 : */
119 0 : static void pim_vxlan_init_work(void)
120 : {
121 0 : if (vxlan_info.flags & PIM_VXLANF_WORK_INITED)
122 : return;
123 :
124 0 : vxlan_info.max_work_cnt = PIM_VXLAN_WORK_MAX;
125 0 : vxlan_info.flags |= PIM_VXLANF_WORK_INITED;
126 0 : vxlan_info.work_list = list_new();
127 0 : pim_vxlan_work_timer_setup(true/* start */);
128 : }
129 :
130 0 : static void pim_vxlan_add_work(struct pim_vxlan_sg *vxlan_sg)
131 : {
132 0 : if (vxlan_sg->flags & PIM_VXLAN_SGF_DEL_IN_PROG) {
133 0 : if (PIM_DEBUG_VXLAN)
134 0 : zlog_debug("vxlan SG %s skip work list; del-in-prog",
135 : vxlan_sg->sg_str);
136 0 : return;
137 : }
138 :
139 0 : pim_vxlan_init_work();
140 :
141 : /* already a part of the work list */
142 0 : if (vxlan_sg->work_node)
143 : return;
144 :
145 0 : if (PIM_DEBUG_VXLAN)
146 0 : zlog_debug("vxlan SG %s work list add",
147 : vxlan_sg->sg_str);
148 0 : vxlan_sg->work_node = listnode_add(vxlan_info.work_list, vxlan_sg);
149 : /* XXX: adjust max_work_cnt if needed */
150 : }
151 :
152 0 : static void pim_vxlan_del_work(struct pim_vxlan_sg *vxlan_sg)
153 : {
154 0 : if (!vxlan_sg->work_node)
155 : return;
156 :
157 0 : if (PIM_DEBUG_VXLAN)
158 0 : zlog_debug("vxlan SG %s work list del",
159 : vxlan_sg->sg_str);
160 :
161 0 : if (vxlan_sg->work_node == vxlan_info.next_work)
162 0 : vxlan_info.next_work = vxlan_sg->work_node->next;
163 :
164 0 : list_delete_node(vxlan_info.work_list, vxlan_sg->work_node);
165 0 : vxlan_sg->work_node = NULL;
166 : }
167 :
168 0 : void pim_vxlan_update_sg_reg_state(struct pim_instance *pim,
169 : struct pim_upstream *up, bool reg_join)
170 : {
171 0 : struct pim_vxlan_sg *vxlan_sg;
172 :
173 0 : vxlan_sg = pim_vxlan_sg_find(pim, &up->sg);
174 0 : if (!vxlan_sg)
175 : return;
176 :
177 : /* add the vxlan sg entry to a work list for periodic reg joins.
178 : * the entry will stay in the list as long as the register state is
179 : * PIM_REG_JOIN
180 : */
181 0 : if (reg_join)
182 0 : pim_vxlan_add_work(vxlan_sg);
183 : else
184 0 : pim_vxlan_del_work(vxlan_sg);
185 : }
186 :
187 0 : static void pim_vxlan_work_timer_cb(struct thread *t)
188 : {
189 0 : pim_vxlan_do_reg_work();
190 0 : pim_vxlan_work_timer_setup(true /* start */);
191 0 : }
192 :
193 : /* global 1second timer used for periodic processing */
194 0 : static void pim_vxlan_work_timer_setup(bool start)
195 : {
196 0 : THREAD_OFF(vxlan_info.work_timer);
197 0 : if (start)
198 0 : thread_add_timer(router->master, pim_vxlan_work_timer_cb, NULL,
199 : PIM_VXLAN_WORK_TIME, &vxlan_info.work_timer);
200 0 : }
201 :
202 : /**************************** vxlan origination mroutes ***********************
203 : * For every (local-vtep-ip, bum-mcast-grp) registered by evpn an origination
204 : * mroute is setup by pimd. The purpose of this mroute is to forward vxlan
205 : * encapsulated BUM (broadcast, unknown-unicast and unknown-multicast packets
206 : * over the underlay.)
207 : *
208 : * Sample mroute (single VTEP):
209 : * (27.0.0.7, 239.1.1.100) Iif: lo Oifs: uplink-1
210 : *
211 : * Sample mroute (anycast VTEP):
212 : * (36.0.0.9, 239.1.1.100) Iif: peerlink-3.4094\
213 : * Oifs: peerlink-3.4094 uplink-1
214 : ***************************************************************************/
215 0 : static void pim_vxlan_orig_mr_up_del(struct pim_vxlan_sg *vxlan_sg)
216 : {
217 0 : struct pim_upstream *up = vxlan_sg->up;
218 :
219 0 : if (!up)
220 : return;
221 :
222 0 : if (PIM_DEBUG_VXLAN)
223 0 : zlog_debug("vxlan SG %s orig mroute-up del",
224 : vxlan_sg->sg_str);
225 :
226 0 : vxlan_sg->up = NULL;
227 :
228 0 : if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG) {
229 : /* clear out all the vxlan properties */
230 0 : up->flags &= ~(PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG |
231 : PIM_UPSTREAM_FLAG_MASK_STATIC_IIF |
232 : PIM_UPSTREAM_FLAG_MASK_DISABLE_KAT_EXPIRY |
233 : PIM_UPSTREAM_FLAG_MASK_FORCE_PIMREG |
234 : PIM_UPSTREAM_FLAG_MASK_NO_PIMREG_DATA |
235 : PIM_UPSTREAM_FLAG_MASK_ALLOW_IIF_IN_OIL);
236 :
237 : /* We bring things to a grinding halt by force expirying
238 : * the kat. Doing this will also remove the reference we
239 : * created as a "vxlan" source and delete the upstream entry
240 : * if there are no other references.
241 : */
242 0 : if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) {
243 0 : THREAD_OFF(up->t_ka_timer);
244 0 : up = pim_upstream_keep_alive_timer_proc(up);
245 : } else {
246 : /* this is really unexpected as we force vxlan
247 : * origination mroutes active sources but just in
248 : * case
249 : */
250 0 : up = pim_upstream_del(vxlan_sg->pim, up, __func__);
251 : }
252 : /* if there are other references register the source
253 : * for nht
254 : */
255 0 : if (up) {
256 0 : enum pim_rpf_result r;
257 :
258 0 : r = pim_rpf_update(vxlan_sg->pim, up, NULL, __func__);
259 0 : if (r == PIM_RPF_FAILURE) {
260 0 : if (PIM_DEBUG_VXLAN)
261 0 : zlog_debug(
262 : "vxlan SG %s rpf_update failure",
263 : vxlan_sg->sg_str);
264 : }
265 : }
266 : }
267 : }
268 :
269 0 : static void pim_vxlan_orig_mr_up_iif_update(struct pim_vxlan_sg *vxlan_sg)
270 : {
271 : /* update MFC with the new IIF */
272 0 : pim_upstream_fill_static_iif(vxlan_sg->up, vxlan_sg->iif);
273 0 : pim_upstream_mroute_iif_update(vxlan_sg->up->channel_oil, __func__);
274 :
275 0 : if (PIM_DEBUG_VXLAN)
276 0 : zlog_debug("vxlan SG %s orig mroute-up updated with iif %s",
277 : vxlan_sg->sg_str,
278 : vxlan_sg->iif?vxlan_sg->iif->name:"-");
279 :
280 0 : }
281 :
282 : /* For every VxLAN BUM multicast group we setup a SG-up that has the following
283 : * "forced properties" -
284 : * 1. Directly connected on a DR interface i.e. we must act as an FHR
285 : * 2. We prime the pump i.e. no multicast data is needed to register this
286 : * source with the FHR. To do that we send periodic null registers if
287 : * the SG entry is in a register-join state. We also prevent expiry of
288 : * KAT.
289 : * 3. As this SG is setup without data there is no need to register encapsulate
290 : * data traffic. This encapsulation is explicitly skipped for the following
291 : * reasons -
292 : * a) Many levels of encapsulation are needed creating MTU disc challenges.
293 : * Overlay BUM is encapsulated in a vxlan/UDP/IP header and then
294 : * encapsulated again in a pim-register header.
295 : * b) On a vxlan-aa setup both switches rx a copy of each BUM packet. if
296 : * they both reg encapsulated traffic the RP will accept the duplicates
297 : * as there are no RPF checks for this encapsulated data.
298 : * a), b) can be workarounded if needed, but there is really no need because
299 : * of (2) i.e. the pump is primed without data.
300 : */
301 0 : static void pim_vxlan_orig_mr_up_add(struct pim_vxlan_sg *vxlan_sg)
302 : {
303 0 : struct pim_upstream *up;
304 0 : struct pim_interface *term_ifp;
305 0 : int flags = 0;
306 0 : struct pim_instance *pim = vxlan_sg->pim;
307 :
308 0 : if (vxlan_sg->up) {
309 : /* nothing to do */
310 : return;
311 : }
312 :
313 0 : if (PIM_DEBUG_VXLAN)
314 0 : zlog_debug("vxlan SG %s orig mroute-up add with iif %s",
315 : vxlan_sg->sg_str,
316 : vxlan_sg->iif?vxlan_sg->iif->name:"-");
317 :
318 0 : PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_ORIG(flags);
319 : /* pin the IIF to lo or peerlink-subinterface and disable NHT */
320 0 : PIM_UPSTREAM_FLAG_SET_STATIC_IIF(flags);
321 : /* Fake traffic by setting SRC_STREAM and starting KAT */
322 : /* We intentionally skip updating ref count for SRC_STREAM/FHR.
323 : * Setting SRC_VXLAN should have already created a reference
324 : * preventing the entry from being deleted
325 : */
326 0 : PIM_UPSTREAM_FLAG_SET_FHR(flags);
327 0 : PIM_UPSTREAM_FLAG_SET_SRC_STREAM(flags);
328 : /* Force pimreg even if non-DR. This is needed on a MLAG setup for
329 : * VxLAN AA
330 : */
331 0 : PIM_UPSTREAM_FLAG_SET_FORCE_PIMREG(flags);
332 : /* prevent KAT expiry. we want the MDT setup even if there is no BUM
333 : * traffic
334 : */
335 0 : PIM_UPSTREAM_FLAG_SET_DISABLE_KAT_EXPIRY(flags);
336 : /* SPT for vxlan BUM groups is primed and maintained via NULL
337 : * registers so there is no need to reg-encapsulate
338 : * vxlan-encapsulated overlay data traffic
339 : */
340 0 : PIM_UPSTREAM_FLAG_SET_NO_PIMREG_DATA(flags);
341 : /* On a MLAG setup we force a copy to the MLAG peer while also
342 : * accepting traffic from the peer. To do this we set peerlink-rif as
343 : * the IIF and also add it to the OIL
344 : */
345 0 : PIM_UPSTREAM_FLAG_SET_ALLOW_IIF_IN_OIL(flags);
346 :
347 : /* XXX: todo: defer pim_upstream add if pim is not enabled on the iif */
348 0 : up = pim_upstream_find(vxlan_sg->pim, &vxlan_sg->sg);
349 0 : if (up) {
350 : /* if the iif is set to something other than the vxlan_sg->iif
351 : * we must dereg the old nexthop and force to new "static"
352 : * iif
353 : */
354 0 : if (!PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up->flags)) {
355 0 : pim_delete_tracked_nexthop(vxlan_sg->pim,
356 : up->upstream_addr, up, NULL);
357 : }
358 : /* We are acting FHR; clear out use_rpt setting if any */
359 0 : pim_upstream_update_use_rpt(up, false /*update_mroute*/);
360 0 : pim_upstream_ref(up, flags, __func__);
361 0 : vxlan_sg->up = up;
362 0 : term_ifp = pim_vxlan_get_term_ifp(pim);
363 : /* mute termination device on origination mroutes */
364 0 : if (term_ifp)
365 0 : pim_channel_update_oif_mute(up->channel_oil,
366 : term_ifp);
367 0 : pim_vxlan_orig_mr_up_iif_update(vxlan_sg);
368 : /* mute pimreg on origination mroutes */
369 0 : if (pim->regiface)
370 0 : pim_channel_update_oif_mute(up->channel_oil,
371 0 : pim->regiface->info);
372 : } else {
373 0 : up = pim_upstream_add(vxlan_sg->pim, &vxlan_sg->sg,
374 : vxlan_sg->iif, flags, __func__, NULL);
375 0 : vxlan_sg->up = up;
376 : }
377 :
378 0 : if (!up) {
379 0 : if (PIM_DEBUG_VXLAN)
380 0 : zlog_debug("vxlan SG %s orig mroute-up add failed",
381 : vxlan_sg->sg_str);
382 0 : return;
383 : }
384 :
385 0 : pim_upstream_keep_alive_timer_start(up, vxlan_sg->pim->keep_alive_time);
386 :
387 : /* register the source with the RP */
388 0 : switch (up->reg_state) {
389 :
390 0 : case PIM_REG_NOINFO:
391 0 : pim_register_join(up);
392 0 : pim_null_register_send(up);
393 0 : break;
394 :
395 0 : case PIM_REG_JOIN:
396 : /* if the pim upstream entry is already in reg-join state
397 : * send null_register right away and add to the register
398 : * worklist
399 : */
400 0 : pim_null_register_send(up);
401 0 : pim_vxlan_update_sg_reg_state(pim, up, true);
402 0 : break;
403 :
404 : case PIM_REG_JOIN_PENDING:
405 : case PIM_REG_PRUNE:
406 : break;
407 : }
408 :
409 : /* update the inherited OIL */
410 0 : pim_upstream_inherited_olist(vxlan_sg->pim, up);
411 0 : if (!up->channel_oil->installed)
412 0 : pim_upstream_mroute_add(up->channel_oil, __func__);
413 : }
414 :
415 0 : static void pim_vxlan_orig_mr_oif_add(struct pim_vxlan_sg *vxlan_sg)
416 : {
417 0 : if (!vxlan_sg->up || !vxlan_sg->orig_oif)
418 : return;
419 :
420 0 : if (PIM_DEBUG_VXLAN)
421 0 : zlog_debug("vxlan SG %s oif %s add",
422 : vxlan_sg->sg_str, vxlan_sg->orig_oif->name);
423 :
424 0 : vxlan_sg->flags |= PIM_VXLAN_SGF_OIF_INSTALLED;
425 0 : pim_channel_add_oif(vxlan_sg->up->channel_oil,
426 : vxlan_sg->orig_oif, PIM_OIF_FLAG_PROTO_VXLAN,
427 : __func__);
428 : }
429 :
430 0 : static void pim_vxlan_orig_mr_oif_del(struct pim_vxlan_sg *vxlan_sg)
431 : {
432 0 : struct interface *orig_oif;
433 :
434 0 : orig_oif = vxlan_sg->orig_oif;
435 0 : vxlan_sg->orig_oif = NULL;
436 :
437 0 : if (!(vxlan_sg->flags & PIM_VXLAN_SGF_OIF_INSTALLED))
438 : return;
439 :
440 0 : if (PIM_DEBUG_VXLAN)
441 0 : zlog_debug("vxlan SG %s oif %s del",
442 : vxlan_sg->sg_str, orig_oif->name);
443 :
444 0 : vxlan_sg->flags &= ~PIM_VXLAN_SGF_OIF_INSTALLED;
445 0 : pim_channel_del_oif(vxlan_sg->up->channel_oil,
446 : orig_oif, PIM_OIF_FLAG_PROTO_VXLAN, __func__);
447 : }
448 :
449 0 : static inline struct interface *pim_vxlan_orig_mr_oif_get(
450 : struct pim_instance *pim)
451 : {
452 0 : return (vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED) ?
453 0 : pim->vxlan.peerlink_rif : NULL;
454 : }
455 :
456 : /* Single VTEPs: IIF for the vxlan-origination-mroutes is lo or vrf-dev (if
457 : * the mroute is in a non-default vrf).
458 : * Anycast VTEPs: IIF is the MLAG ISL/peerlink.
459 : */
460 8 : static inline struct interface *pim_vxlan_orig_mr_iif_get(
461 : struct pim_instance *pim)
462 : {
463 0 : return ((vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED) &&
464 0 : pim->vxlan.peerlink_rif) ?
465 4 : pim->vxlan.peerlink_rif : pim->vxlan.default_iif;
466 : }
467 :
468 0 : static bool pim_vxlan_orig_mr_add_is_ok(struct pim_vxlan_sg *vxlan_sg)
469 : {
470 0 : struct pim_interface *pim_ifp;
471 :
472 0 : vxlan_sg->iif = pim_vxlan_orig_mr_iif_get(vxlan_sg->pim);
473 0 : if (!vxlan_sg->iif)
474 : return false;
475 :
476 0 : pim_ifp = (struct pim_interface *)vxlan_sg->iif->info;
477 0 : if (!pim_ifp || (pim_ifp->mroute_vif_index < 0))
478 0 : return false;
479 :
480 : return true;
481 : }
482 :
483 0 : static void pim_vxlan_orig_mr_install(struct pim_vxlan_sg *vxlan_sg)
484 : {
485 0 : pim_vxlan_orig_mr_up_add(vxlan_sg);
486 :
487 0 : vxlan_sg->orig_oif = pim_vxlan_orig_mr_oif_get(vxlan_sg->pim);
488 0 : pim_vxlan_orig_mr_oif_add(vxlan_sg);
489 0 : }
490 :
491 0 : static void pim_vxlan_orig_mr_add(struct pim_vxlan_sg *vxlan_sg)
492 : {
493 0 : if (!pim_vxlan_orig_mr_add_is_ok(vxlan_sg))
494 : return;
495 :
496 0 : if (PIM_DEBUG_VXLAN)
497 0 : zlog_debug("vxlan SG %s orig-mr add", vxlan_sg->sg_str);
498 :
499 0 : pim_vxlan_orig_mr_install(vxlan_sg);
500 : }
501 :
502 0 : static void pim_vxlan_orig_mr_del(struct pim_vxlan_sg *vxlan_sg)
503 : {
504 0 : if (PIM_DEBUG_VXLAN)
505 0 : zlog_debug("vxlan SG %s orig-mr del", vxlan_sg->sg_str);
506 :
507 0 : pim_vxlan_orig_mr_oif_del(vxlan_sg);
508 0 : pim_vxlan_orig_mr_up_del(vxlan_sg);
509 0 : }
510 :
511 0 : static void pim_vxlan_orig_mr_iif_update(struct hash_bucket *bucket, void *arg)
512 : {
513 0 : struct interface *ifp;
514 0 : struct pim_vxlan_sg *vxlan_sg = (struct pim_vxlan_sg *)bucket->data;
515 0 : struct interface *old_iif = vxlan_sg->iif;
516 :
517 0 : if (!pim_vxlan_is_orig_mroute(vxlan_sg))
518 : return;
519 :
520 0 : ifp = pim_vxlan_orig_mr_iif_get(vxlan_sg->pim);
521 0 : if (PIM_DEBUG_VXLAN)
522 0 : zlog_debug("vxlan SG %s iif changed from %s to %s",
523 : vxlan_sg->sg_str,
524 : old_iif ? old_iif->name : "-",
525 : ifp ? ifp->name : "-");
526 :
527 0 : if (pim_vxlan_orig_mr_add_is_ok(vxlan_sg)) {
528 0 : if (vxlan_sg->up) {
529 : /* upstream exists but iif changed */
530 0 : pim_vxlan_orig_mr_up_iif_update(vxlan_sg);
531 : } else {
532 : /* install mroute */
533 0 : pim_vxlan_orig_mr_install(vxlan_sg);
534 : }
535 : } else {
536 0 : pim_vxlan_orig_mr_del(vxlan_sg);
537 : }
538 : }
539 :
540 : /**************************** vxlan termination mroutes ***********************
541 : * For every bum-mcast-grp registered by evpn a *G termination
542 : * mroute is setup by pimd. The purpose of this mroute is to pull down vxlan
543 : * packets with the bum-mcast-grp dip from the underlay and terminate the
544 : * tunnel. This is done by including the vxlan termination device (ipmr-lo) in
545 : * its OIL. The vxlan de-capsulated packets are subject to subsequent overlay
546 : * bridging.
547 : *
548 : * Sample mroute:
549 : * (0.0.0.0, 239.1.1.100) Iif: uplink-1 Oifs: ipmr-lo, uplink-1
550 : *****************************************************************************/
551 3 : struct pim_interface *pim_vxlan_get_term_ifp(struct pim_instance *pim)
552 : {
553 3 : return pim->vxlan.term_if ?
554 3 : (struct pim_interface *)pim->vxlan.term_if->info : NULL;
555 : }
556 :
557 0 : static void pim_vxlan_term_mr_oif_add(struct pim_vxlan_sg *vxlan_sg)
558 : {
559 0 : if (vxlan_sg->flags & PIM_VXLAN_SGF_OIF_INSTALLED)
560 : return;
561 :
562 0 : if (PIM_DEBUG_VXLAN)
563 0 : zlog_debug("vxlan SG %s term-oif %s add",
564 : vxlan_sg->sg_str, vxlan_sg->term_oif->name);
565 :
566 0 : if (pim_ifchannel_local_membership_add(vxlan_sg->term_oif,
567 : &vxlan_sg->sg, true /*is_vxlan */)) {
568 0 : vxlan_sg->flags |= PIM_VXLAN_SGF_OIF_INSTALLED;
569 : /* update the inherited OIL */
570 : /* XXX - I don't see the inherited OIL updated when a local
571 : * member is added. And that probably needs to be fixed. Till
572 : * that happens we do a force update on the inherited OIL
573 : * here.
574 : */
575 0 : pim_upstream_inherited_olist(vxlan_sg->pim, vxlan_sg->up);
576 : } else {
577 0 : zlog_warn("vxlan SG %s term-oif %s add failed",
578 : vxlan_sg->sg_str, vxlan_sg->term_oif->name);
579 : }
580 : }
581 :
582 0 : static void pim_vxlan_term_mr_oif_del(struct pim_vxlan_sg *vxlan_sg)
583 : {
584 0 : if (!(vxlan_sg->flags & PIM_VXLAN_SGF_OIF_INSTALLED))
585 : return;
586 :
587 0 : if (PIM_DEBUG_VXLAN)
588 0 : zlog_debug("vxlan SG %s oif %s del",
589 : vxlan_sg->sg_str, vxlan_sg->term_oif->name);
590 :
591 0 : vxlan_sg->flags &= ~PIM_VXLAN_SGF_OIF_INSTALLED;
592 0 : pim_ifchannel_local_membership_del(vxlan_sg->term_oif, &vxlan_sg->sg);
593 : /* update the inherited OIL */
594 : /* XXX - I don't see the inherited OIL updated when a local member
595 : * is deleted. And that probably needs to be fixed. Till that happens
596 : * we do a force update on the inherited OIL here.
597 : */
598 0 : pim_upstream_inherited_olist(vxlan_sg->pim, vxlan_sg->up);
599 : }
600 :
601 : static void pim_vxlan_update_sg_entry_mlag(struct pim_instance *pim,
602 : struct pim_upstream *up, bool inherit)
603 : {
604 : bool is_df = true;
605 :
606 : if (inherit && up->parent &&
607 : PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up->parent->flags) &&
608 : PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->parent->flags))
609 : is_df = false;
610 :
611 : pim_mlag_up_df_role_update(pim, up, is_df, "inherit_xg_df");
612 : }
613 :
614 : /* We run MLAG DF election only on mroutes that have the termination
615 : * device ipmr-lo in the immediate OIL. This is only (*, G) entries at the
616 : * moment. For (S, G) entries that (with ipmr-lo in the inherited OIL) we
617 : * inherit the DF role from the (*, G) entry.
618 : */
619 0 : void pim_vxlan_inherit_mlag_flags(struct pim_instance *pim,
620 : struct pim_upstream *up, bool inherit)
621 : {
622 0 : struct listnode *listnode;
623 0 : struct pim_upstream *child;
624 :
625 0 : for (ALL_LIST_ELEMENTS_RO(up->sources, listnode,
626 : child)) {
627 0 : pim_vxlan_update_sg_entry_mlag(pim,
628 : child, true /* inherit */);
629 : }
630 0 : }
631 :
632 0 : static void pim_vxlan_term_mr_up_add(struct pim_vxlan_sg *vxlan_sg)
633 : {
634 0 : struct pim_upstream *up;
635 0 : int flags = 0;
636 :
637 0 : if (vxlan_sg->up) {
638 : /* nothing to do */
639 : return;
640 : }
641 :
642 0 : if (PIM_DEBUG_VXLAN)
643 0 : zlog_debug("vxlan SG %s term mroute-up add",
644 : vxlan_sg->sg_str);
645 :
646 0 : PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_TERM(flags);
647 : /* enable MLAG designated-forwarder election on termination mroutes */
648 0 : PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(flags);
649 :
650 0 : up = pim_upstream_add(vxlan_sg->pim, &vxlan_sg->sg, NULL /* iif */,
651 : flags, __func__, NULL);
652 0 : vxlan_sg->up = up;
653 :
654 0 : if (!up) {
655 0 : zlog_warn("vxlan SG %s term mroute-up add failed",
656 : vxlan_sg->sg_str);
657 0 : return;
658 : }
659 :
660 : /* update existing SG entries with the parent's MLAG flag */
661 0 : pim_vxlan_inherit_mlag_flags(vxlan_sg->pim, up, true /*enable*/);
662 : }
663 :
664 0 : static void pim_vxlan_term_mr_up_del(struct pim_vxlan_sg *vxlan_sg)
665 : {
666 0 : struct pim_upstream *up = vxlan_sg->up;
667 :
668 0 : if (!up)
669 : return;
670 :
671 0 : if (PIM_DEBUG_VXLAN)
672 0 : zlog_debug("vxlan SG %s term mroute-up del",
673 : vxlan_sg->sg_str);
674 0 : vxlan_sg->up = NULL;
675 0 : if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM) {
676 : /* update SG entries that are inheriting from this XG entry */
677 0 : pim_vxlan_inherit_mlag_flags(vxlan_sg->pim, up,
678 : false /*enable*/);
679 : /* clear out all the vxlan related flags */
680 0 : up->flags &= ~(PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM |
681 : PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN);
682 0 : pim_mlag_up_local_del(vxlan_sg->pim, up);
683 0 : pim_upstream_del(vxlan_sg->pim, up, __func__);
684 : }
685 : }
686 :
687 0 : static void pim_vxlan_term_mr_add(struct pim_vxlan_sg *vxlan_sg)
688 : {
689 0 : if (PIM_DEBUG_VXLAN)
690 0 : zlog_debug("vxlan SG %s term mroute add", vxlan_sg->sg_str);
691 :
692 0 : vxlan_sg->term_oif = vxlan_sg->pim->vxlan.term_if;
693 0 : if (!vxlan_sg->term_oif)
694 : /* defer termination mroute till we have a termination device */
695 : return;
696 :
697 0 : pim_vxlan_term_mr_up_add(vxlan_sg);
698 : /* set up local membership for the term-oif */
699 0 : pim_vxlan_term_mr_oif_add(vxlan_sg);
700 : }
701 :
702 0 : static void pim_vxlan_term_mr_del(struct pim_vxlan_sg *vxlan_sg)
703 : {
704 0 : if (PIM_DEBUG_VXLAN)
705 0 : zlog_debug("vxlan SG %s term mroute del", vxlan_sg->sg_str);
706 :
707 : /* remove local membership associated with the term oif */
708 0 : pim_vxlan_term_mr_oif_del(vxlan_sg);
709 : /* remove references to the upstream entry */
710 0 : pim_vxlan_term_mr_up_del(vxlan_sg);
711 0 : }
712 :
713 : /************************** vxlan SG cache management ************************/
714 0 : static unsigned int pim_vxlan_sg_hash_key_make(const void *p)
715 : {
716 0 : const struct pim_vxlan_sg *vxlan_sg = p;
717 :
718 0 : return pim_sgaddr_hash(vxlan_sg->sg, 0);
719 : }
720 :
721 0 : static bool pim_vxlan_sg_hash_eq(const void *p1, const void *p2)
722 : {
723 0 : const struct pim_vxlan_sg *sg1 = p1;
724 0 : const struct pim_vxlan_sg *sg2 = p2;
725 :
726 0 : return !pim_sgaddr_cmp(sg1->sg, sg2->sg);
727 : }
728 :
729 0 : static struct pim_vxlan_sg *pim_vxlan_sg_new(struct pim_instance *pim,
730 : pim_sgaddr *sg)
731 : {
732 0 : struct pim_vxlan_sg *vxlan_sg;
733 :
734 0 : vxlan_sg = XCALLOC(MTYPE_PIM_VXLAN_SG, sizeof(*vxlan_sg));
735 :
736 0 : vxlan_sg->pim = pim;
737 0 : vxlan_sg->sg = *sg;
738 0 : snprintfrr(vxlan_sg->sg_str, sizeof(vxlan_sg->sg_str), "%pSG", sg);
739 :
740 0 : if (PIM_DEBUG_VXLAN)
741 0 : zlog_debug("vxlan SG %s alloc", vxlan_sg->sg_str);
742 :
743 0 : vxlan_sg = hash_get(pim->vxlan.sg_hash, vxlan_sg, hash_alloc_intern);
744 :
745 : /* we register with the MLAG daemon in the first VxLAN SG and never
746 : * de-register during that life of the pimd
747 : */
748 0 : if (pim->vxlan.sg_hash->count == 1) {
749 0 : vxlan_mlag.flags |= PIM_VXLAN_MLAGF_DO_REG;
750 0 : pim_mlag_register();
751 : }
752 :
753 0 : return vxlan_sg;
754 : }
755 :
756 0 : struct pim_vxlan_sg *pim_vxlan_sg_find(struct pim_instance *pim, pim_sgaddr *sg)
757 : {
758 0 : struct pim_vxlan_sg lookup;
759 :
760 0 : lookup.sg = *sg;
761 0 : return hash_lookup(pim->vxlan.sg_hash, &lookup);
762 : }
763 :
764 0 : struct pim_vxlan_sg *pim_vxlan_sg_add(struct pim_instance *pim, pim_sgaddr *sg)
765 : {
766 0 : struct pim_vxlan_sg *vxlan_sg;
767 :
768 0 : vxlan_sg = pim_vxlan_sg_find(pim, sg);
769 0 : if (vxlan_sg)
770 : return vxlan_sg;
771 :
772 0 : vxlan_sg = pim_vxlan_sg_new(pim, sg);
773 :
774 0 : if (pim_vxlan_is_orig_mroute(vxlan_sg))
775 0 : pim_vxlan_orig_mr_add(vxlan_sg);
776 : else
777 0 : pim_vxlan_term_mr_add(vxlan_sg);
778 :
779 : return vxlan_sg;
780 : }
781 :
782 0 : static void pim_vxlan_sg_del_item(struct pim_vxlan_sg *vxlan_sg)
783 : {
784 0 : vxlan_sg->flags |= PIM_VXLAN_SGF_DEL_IN_PROG;
785 :
786 0 : pim_vxlan_del_work(vxlan_sg);
787 :
788 0 : if (pim_vxlan_is_orig_mroute(vxlan_sg))
789 0 : pim_vxlan_orig_mr_del(vxlan_sg);
790 : else
791 0 : pim_vxlan_term_mr_del(vxlan_sg);
792 :
793 0 : if (PIM_DEBUG_VXLAN)
794 0 : zlog_debug("vxlan SG %s free", vxlan_sg->sg_str);
795 :
796 0 : XFREE(MTYPE_PIM_VXLAN_SG, vxlan_sg);
797 0 : }
798 :
799 0 : void pim_vxlan_sg_del(struct pim_instance *pim, pim_sgaddr *sg)
800 : {
801 0 : struct pim_vxlan_sg *vxlan_sg;
802 :
803 0 : vxlan_sg = pim_vxlan_sg_find(pim, sg);
804 0 : if (!vxlan_sg)
805 : return;
806 :
807 0 : hash_release(pim->vxlan.sg_hash, vxlan_sg);
808 0 : pim_vxlan_sg_del_item(vxlan_sg);
809 : }
810 :
811 : /******************************* MLAG handling *******************************/
812 0 : bool pim_vxlan_do_mlag_reg(void)
813 : {
814 0 : return (vxlan_mlag.flags & PIM_VXLAN_MLAGF_DO_REG);
815 : }
816 :
817 : /* The peerlink sub-interface is added as an OIF to the origination-mroute.
818 : * This is done to send a copy of the multicast-vxlan encapsulated traffic
819 : * to the MLAG peer which may mroute it over the underlay if there are any
820 : * interested receivers.
821 : */
822 0 : static void pim_vxlan_sg_peerlink_oif_update(struct hash_bucket *bucket,
823 : void *arg)
824 : {
825 0 : struct interface *new_oif = (struct interface *)arg;
826 0 : struct pim_vxlan_sg *vxlan_sg = (struct pim_vxlan_sg *)bucket->data;
827 :
828 0 : if (!pim_vxlan_is_orig_mroute(vxlan_sg))
829 : return;
830 :
831 0 : if (vxlan_sg->orig_oif == new_oif)
832 : return;
833 :
834 0 : pim_vxlan_orig_mr_oif_del(vxlan_sg);
835 :
836 0 : vxlan_sg->orig_oif = new_oif;
837 0 : pim_vxlan_orig_mr_oif_add(vxlan_sg);
838 : }
839 :
840 : /* In the case of anycast VTEPs the VTEP-PIP must be used as the
841 : * register source.
842 : */
843 0 : bool pim_vxlan_get_register_src(struct pim_instance *pim,
844 : struct pim_upstream *up, struct in_addr *src_p)
845 : {
846 0 : if (!(vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED))
847 : return true;
848 :
849 : /* if address is not available suppress the pim-register */
850 0 : if (vxlan_mlag.reg_addr.s_addr == INADDR_ANY)
851 : return false;
852 :
853 0 : *src_p = vxlan_mlag.reg_addr;
854 0 : return true;
855 : }
856 :
857 0 : void pim_vxlan_mlag_update(bool enable, bool peer_state, uint32_t role,
858 : struct interface *peerlink_rif,
859 : struct in_addr *reg_addr)
860 : {
861 0 : struct pim_instance *pim;
862 0 : char addr_buf[INET_ADDRSTRLEN];
863 0 : struct pim_interface *pim_ifp = NULL;
864 :
865 0 : if (PIM_DEBUG_VXLAN) {
866 0 : inet_ntop(AF_INET, reg_addr,
867 : addr_buf, INET_ADDRSTRLEN);
868 0 : zlog_debug("vxlan MLAG update %s state %s role %d rif %s addr %s",
869 : enable ? "enable" : "disable",
870 : peer_state ? "up" : "down",
871 : role,
872 : peerlink_rif ? peerlink_rif->name : "-",
873 : addr_buf);
874 : }
875 :
876 : /* XXX: for now vxlan termination is only possible in the default VRF
877 : * when that changes this will need to change to iterate all VRFs
878 : */
879 0 : pim = pim_get_pim_instance(VRF_DEFAULT);
880 :
881 0 : if (!pim) {
882 0 : if (PIM_DEBUG_VXLAN)
883 0 : zlog_debug("%s: Unable to find pim instance", __func__);
884 0 : return;
885 : }
886 :
887 0 : if (enable)
888 0 : vxlan_mlag.flags |= PIM_VXLAN_MLAGF_ENABLED;
889 : else
890 0 : vxlan_mlag.flags &= ~PIM_VXLAN_MLAGF_ENABLED;
891 :
892 0 : if (vxlan_mlag.peerlink_rif != peerlink_rif)
893 0 : vxlan_mlag.peerlink_rif = peerlink_rif;
894 :
895 0 : vxlan_mlag.reg_addr = *reg_addr;
896 0 : vxlan_mlag.peer_state = peer_state;
897 0 : vxlan_mlag.role = role;
898 :
899 : /* process changes */
900 0 : if (vxlan_mlag.peerlink_rif)
901 0 : pim_ifp = (struct pim_interface *)vxlan_mlag.peerlink_rif->info;
902 0 : if ((vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED) &&
903 0 : pim_ifp && (pim_ifp->mroute_vif_index > 0))
904 0 : pim_vxlan_set_peerlink_rif(pim, peerlink_rif);
905 : else
906 0 : pim_vxlan_set_peerlink_rif(pim, NULL);
907 : }
908 :
909 : /****************************** misc callbacks *******************************/
910 4 : static void pim_vxlan_set_default_iif(struct pim_instance *pim,
911 : struct interface *ifp)
912 : {
913 4 : struct interface *old_iif;
914 :
915 4 : if (pim->vxlan.default_iif == ifp)
916 : return;
917 :
918 4 : old_iif = pim->vxlan.default_iif;
919 4 : if (PIM_DEBUG_VXLAN)
920 0 : zlog_debug("%s: vxlan default iif changed from %s to %s",
921 : __func__, old_iif ? old_iif->name : "-",
922 : ifp ? ifp->name : "-");
923 :
924 4 : old_iif = pim_vxlan_orig_mr_iif_get(pim);
925 4 : pim->vxlan.default_iif = ifp;
926 4 : ifp = pim_vxlan_orig_mr_iif_get(pim);
927 4 : if (old_iif == ifp)
928 : return;
929 :
930 4 : if (PIM_DEBUG_VXLAN)
931 0 : zlog_debug("%s: vxlan orig iif changed from %s to %s", __func__,
932 : old_iif ? old_iif->name : "-",
933 : ifp ? ifp->name : "-");
934 :
935 : /* add/del upstream entries for the existing vxlan SG when the
936 : * interface becomes available
937 : */
938 4 : if (pim->vxlan.sg_hash)
939 2 : hash_iterate(pim->vxlan.sg_hash,
940 : pim_vxlan_orig_mr_iif_update, NULL);
941 : }
942 :
943 0 : static void pim_vxlan_up_cost_update(struct pim_instance *pim,
944 : struct pim_upstream *up,
945 : struct interface *old_peerlink_rif)
946 : {
947 0 : if (!PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up->flags))
948 : return;
949 :
950 0 : if (up->rpf.source_nexthop.interface &&
951 : ((up->rpf.source_nexthop.interface ==
952 0 : pim->vxlan.peerlink_rif) ||
953 : (up->rpf.source_nexthop.interface ==
954 : old_peerlink_rif))) {
955 0 : if (PIM_DEBUG_VXLAN)
956 0 : zlog_debug("RPF cost adjust for %s on peerlink-rif (old: %s, new: %s) change",
957 : up->sg_str,
958 : old_peerlink_rif ?
959 : old_peerlink_rif->name : "-",
960 : pim->vxlan.peerlink_rif ?
961 : pim->vxlan.peerlink_rif->name : "-");
962 : pim_mlag_up_local_add(pim, up);
963 : }
964 : }
965 :
966 0 : static void pim_vxlan_term_mr_cost_update(struct hash_bucket *bucket, void *arg)
967 : {
968 0 : struct interface *old_peerlink_rif = (struct interface *)arg;
969 0 : struct pim_vxlan_sg *vxlan_sg = (struct pim_vxlan_sg *)bucket->data;
970 0 : struct pim_upstream *up;
971 0 : struct listnode *listnode;
972 0 : struct pim_upstream *child;
973 :
974 0 : if (pim_vxlan_is_orig_mroute(vxlan_sg))
975 : return;
976 :
977 : /* Lookup all XG and SG entries with RPF-interface peerlink_rif */
978 0 : up = vxlan_sg->up;
979 0 : if (!up)
980 : return;
981 :
982 0 : pim_vxlan_up_cost_update(vxlan_sg->pim, up,
983 : old_peerlink_rif);
984 :
985 0 : for (ALL_LIST_ELEMENTS_RO(up->sources, listnode,
986 : child))
987 0 : pim_vxlan_up_cost_update(vxlan_sg->pim, child,
988 : old_peerlink_rif);
989 : }
990 :
991 0 : static void pim_vxlan_sg_peerlink_rif_update(struct hash_bucket *bucket,
992 : void *arg)
993 : {
994 0 : pim_vxlan_orig_mr_iif_update(bucket, NULL);
995 0 : pim_vxlan_term_mr_cost_update(bucket, arg);
996 0 : }
997 :
998 0 : static void pim_vxlan_set_peerlink_rif(struct pim_instance *pim,
999 : struct interface *ifp)
1000 : {
1001 0 : struct interface *old_iif;
1002 0 : struct interface *new_iif;
1003 0 : struct interface *old_oif;
1004 0 : struct interface *new_oif;
1005 :
1006 0 : if (pim->vxlan.peerlink_rif == ifp)
1007 : return;
1008 :
1009 0 : old_iif = pim->vxlan.peerlink_rif;
1010 0 : if (PIM_DEBUG_VXLAN)
1011 0 : zlog_debug("%s: vxlan peerlink_rif changed from %s to %s",
1012 : __func__, old_iif ? old_iif->name : "-",
1013 : ifp ? ifp->name : "-");
1014 :
1015 0 : old_iif = pim_vxlan_orig_mr_iif_get(pim);
1016 0 : old_oif = pim_vxlan_orig_mr_oif_get(pim);
1017 0 : pim->vxlan.peerlink_rif = ifp;
1018 :
1019 0 : new_iif = pim_vxlan_orig_mr_iif_get(pim);
1020 0 : if (old_iif != new_iif) {
1021 0 : if (PIM_DEBUG_VXLAN)
1022 0 : zlog_debug("%s: vxlan orig iif changed from %s to %s",
1023 : __func__, old_iif ? old_iif->name : "-",
1024 : new_iif ? new_iif->name : "-");
1025 :
1026 : /* add/del upstream entries for the existing vxlan SG when the
1027 : * interface becomes available
1028 : */
1029 0 : if (pim->vxlan.sg_hash)
1030 0 : hash_iterate(pim->vxlan.sg_hash,
1031 : pim_vxlan_sg_peerlink_rif_update,
1032 : old_iif);
1033 : }
1034 :
1035 0 : new_oif = pim_vxlan_orig_mr_oif_get(pim);
1036 0 : if (old_oif != new_oif) {
1037 0 : if (PIM_DEBUG_VXLAN)
1038 0 : zlog_debug("%s: vxlan orig oif changed from %s to %s",
1039 : __func__, old_oif ? old_oif->name : "-",
1040 : new_oif ? new_oif->name : "-");
1041 0 : if (pim->vxlan.sg_hash)
1042 0 : hash_iterate(pim->vxlan.sg_hash,
1043 : pim_vxlan_sg_peerlink_oif_update,
1044 : new_oif);
1045 : }
1046 : }
1047 :
1048 0 : static void pim_vxlan_term_mr_oif_update(struct hash_bucket *bucket, void *arg)
1049 : {
1050 0 : struct interface *ifp = (struct interface *)arg;
1051 0 : struct pim_vxlan_sg *vxlan_sg = (struct pim_vxlan_sg *)bucket->data;
1052 :
1053 0 : if (pim_vxlan_is_orig_mroute(vxlan_sg))
1054 : return;
1055 :
1056 0 : if (vxlan_sg->term_oif == ifp)
1057 : return;
1058 :
1059 0 : if (PIM_DEBUG_VXLAN)
1060 0 : zlog_debug("vxlan SG %s term oif changed from %s to %s",
1061 : vxlan_sg->sg_str,
1062 : vxlan_sg->term_oif ? vxlan_sg->term_oif->name : "-",
1063 : ifp ? ifp->name : "-");
1064 :
1065 0 : pim_vxlan_term_mr_del(vxlan_sg);
1066 0 : vxlan_sg->term_oif = ifp;
1067 0 : pim_vxlan_term_mr_add(vxlan_sg);
1068 : }
1069 :
1070 0 : static void pim_vxlan_term_oif_update(struct pim_instance *pim,
1071 : struct interface *ifp)
1072 : {
1073 0 : if (pim->vxlan.term_if == ifp)
1074 : return;
1075 :
1076 0 : if (PIM_DEBUG_VXLAN)
1077 0 : zlog_debug("vxlan term oif changed from %s to %s",
1078 : pim->vxlan.term_if ? pim->vxlan.term_if->name : "-",
1079 : ifp ? ifp->name : "-");
1080 :
1081 0 : pim->vxlan.term_if = ifp;
1082 0 : if (pim->vxlan.sg_hash)
1083 0 : hash_iterate(pim->vxlan.sg_hash,
1084 : pim_vxlan_term_mr_oif_update, ifp);
1085 : }
1086 :
1087 9 : void pim_vxlan_add_vif(struct interface *ifp)
1088 : {
1089 9 : struct pim_interface *pim_ifp = ifp->info;
1090 9 : struct pim_instance *pim = pim_ifp->pim;
1091 :
1092 9 : if (pim->vrf->vrf_id != VRF_DEFAULT)
1093 : return;
1094 :
1095 9 : if (if_is_loopback(ifp))
1096 2 : pim_vxlan_set_default_iif(pim, ifp);
1097 :
1098 9 : if (vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED &&
1099 0 : (ifp == vxlan_mlag.peerlink_rif))
1100 0 : pim_vxlan_set_peerlink_rif(pim, ifp);
1101 :
1102 9 : if (pim->vxlan.term_if_cfg == ifp)
1103 0 : pim_vxlan_term_oif_update(pim, ifp);
1104 : }
1105 :
1106 7 : void pim_vxlan_del_vif(struct interface *ifp)
1107 : {
1108 7 : struct pim_interface *pim_ifp = ifp->info;
1109 7 : struct pim_instance *pim = pim_ifp->pim;
1110 :
1111 7 : if (pim->vrf->vrf_id != VRF_DEFAULT)
1112 : return;
1113 :
1114 7 : if (pim->vxlan.default_iif == ifp)
1115 2 : pim_vxlan_set_default_iif(pim, NULL);
1116 :
1117 7 : if (pim->vxlan.peerlink_rif == ifp)
1118 0 : pim_vxlan_set_peerlink_rif(pim, NULL);
1119 :
1120 7 : if (pim->vxlan.term_if == ifp)
1121 0 : pim_vxlan_term_oif_update(pim, NULL);
1122 : }
1123 :
1124 : /* enable pim implicitly on the termination device add */
1125 0 : void pim_vxlan_add_term_dev(struct pim_instance *pim,
1126 : struct interface *ifp)
1127 : {
1128 0 : struct pim_interface *pim_ifp;
1129 :
1130 0 : if (pim->vxlan.term_if_cfg == ifp)
1131 : return;
1132 :
1133 0 : if (PIM_DEBUG_VXLAN)
1134 0 : zlog_debug("vxlan term oif cfg changed from %s to %s",
1135 : pim->vxlan.term_if_cfg ?
1136 : pim->vxlan.term_if_cfg->name : "-",
1137 : ifp->name);
1138 :
1139 0 : pim->vxlan.term_if_cfg = ifp;
1140 :
1141 : /* enable pim on the term ifp */
1142 0 : pim_ifp = (struct pim_interface *)ifp->info;
1143 0 : if (pim_ifp) {
1144 0 : pim_ifp->pim_enable = true;
1145 : /* ifp is already oper up; activate it as a term dev */
1146 0 : if (pim_ifp->mroute_vif_index >= 0)
1147 0 : pim_vxlan_term_oif_update(pim, ifp);
1148 : } else {
1149 : /* ensure that pimreg exists before using the newly created
1150 : * vxlan termination device
1151 : */
1152 0 : pim_if_create_pimreg(pim);
1153 0 : (void)pim_if_new(ifp, false /*igmp*/, true /*pim*/,
1154 : false /*pimreg*/, true /*vxlan_term*/);
1155 : }
1156 : }
1157 :
1158 : /* disable pim implicitly, if needed, on the termination device deletion */
1159 0 : void pim_vxlan_del_term_dev(struct pim_instance *pim)
1160 : {
1161 0 : struct interface *ifp = pim->vxlan.term_if_cfg;
1162 0 : struct pim_interface *pim_ifp;
1163 :
1164 0 : if (PIM_DEBUG_VXLAN)
1165 0 : zlog_debug("vxlan term oif cfg changed from %s to -",
1166 : ifp->name);
1167 :
1168 0 : pim->vxlan.term_if_cfg = NULL;
1169 :
1170 0 : pim_ifp = (struct pim_interface *)ifp->info;
1171 0 : if (pim_ifp) {
1172 0 : pim_ifp->pim_enable = false;
1173 0 : if (!pim_ifp->gm_enable)
1174 0 : pim_if_delete(ifp);
1175 : }
1176 0 : }
1177 :
1178 2 : void pim_vxlan_init(struct pim_instance *pim)
1179 : {
1180 2 : char hash_name[64];
1181 :
1182 2 : snprintf(hash_name, sizeof(hash_name),
1183 2 : "PIM %s vxlan SG hash", pim->vrf->name);
1184 2 : pim->vxlan.sg_hash = hash_create(pim_vxlan_sg_hash_key_make,
1185 : pim_vxlan_sg_hash_eq, hash_name);
1186 2 : }
1187 :
1188 2 : void pim_vxlan_exit(struct pim_instance *pim)
1189 : {
1190 2 : if (pim->vxlan.sg_hash) {
1191 2 : hash_clean(pim->vxlan.sg_hash,
1192 : (void (*)(void *))pim_vxlan_sg_del_item);
1193 2 : hash_free(pim->vxlan.sg_hash);
1194 2 : pim->vxlan.sg_hash = NULL;
1195 : }
1196 2 : }
1197 :
1198 0 : void pim_vxlan_terminate(void)
1199 : {
1200 0 : pim_vxlan_work_timer_setup(false);
1201 0 : }
|