Coverage Report

Created: 2025-11-11 06:17

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