Skip to content

Commit 3b2930c

Browse files
committed
zebra: fix l3vni to l2vni transition
when the vni is moved from l3 to l2vni, the vxlan interface belonging to the l3 was still holding this vni in its table. when this vxlan interfae is assigned new vni, it would go ahead remove the old vni from its table. while removing it asserts beacuse the vxaln interface belonging to this vni is now pointing to l2vni vxlan interface. Fix: when the vni is transition from l3 to l2, remove the vni binging from the vxlan interface belong to the l3. 1. removing the vni info from the hash which is primarly maintain to have cache of netlink notification for SVD to vlan-vni mapping. The hash values should only be removed when vni mapping is removed for vxlan device or vxlan interface is removed by Kernel. The vni info removal lead to an issue where when the same vni is transition back to L3VNI, all the info is lost hence L3VNI does not come up properly. 2. while the vni info removed from hash, the vni info was not freed lead to memory leak. To have proper vni transition, instead of removing vni info from hash, simply dereference from vxlan vlan infomration and when readded as L3VNI restore the referecen for vlan vlan information. Related Ticket: #4404076 Ticket: #4667654 Testing: Before fix: torm-21(config)# vrf vrf1 torm-21(config-vrf)# no vni 4000 torm-21(config-vrf)# do show evpn vni 4000 L2 vxlan99 10 34 4 vrf1 0 br_l3vni After fix: torm-21(config-vrf)# do show evpn vni 4000 L3 vxlan99 0 0 n/a vrf1 2501 br_l3vni torm-21(config)# vrf vrf1 torm-21(config-vrf)# no vni 4000 torm-21(config-vrf)# do show evpn vni 4000 L2 vxlan99 10 34 4 vrf1 2501 br_l3vni torm-21(config-vrf)# vni 4000 torm-21(config-vrf)# do show evpn vni 4000 L3 vxlan99 4 4 n/a vrf1 2501 br_l3vni Detail output after vni transition: torm-21(config-vrf)# do show evpn vni 4000 VNI: 4000 Type: L2 Vlan: 2501 Bridge: br_l3vni Tenant VRF: vrf1 VxLAN interface: vxlan99 VxLAN ifIndex: 27 SVI interface: vlan2501_l3 SVI ifIndex: 29 Local VTEP IP: 2001:c001:ff:f00d::6 Mcast group: 0.0.0.0 Remote VTEPs for this VNI: 2001:c001:ff:f00d::7 flood: - 2001:c001:ff:f00d::5 flood: - 2001:c001:ff:f00d::4 flood: - 2001:cf11:ff:f00d::3 flood: - Number of MACs (local and remote) known for this VNI: 10 Number of ARPs (IPv4 and IPv6, local and remote) known for this VNI: 31 Advertise-gw-macip: No Advertise-svi-macip: No torm-21(config-vrf)# vni 4000 torm-21(config-vrf)# do show evpn vni 4000 VNI: 4000 Type: L3 Tenant VRF: vrf1 Vlan: 2501 Bridge: br_l3vni Local Vtep Ip: 2001:c001:ff:f00d::6 Vxlan-Intf: vxlan99 SVI-If: vlan2501_l3 State: Up VNI Filter: none System MAC: 44:38:39:ff:ff:18 Router MAC: 44:38:39:ff:ff:18 Number of MACs (local and remote) known for this VNI: 4 Number of ARPs (IPv4 and IPv6, local and remote) known for this VNI: 4 L2 VNIs: 1000 1001 1002 1003 Signed-off-by: Chirag Shah <[email protected]>
1 parent aa677ff commit 3b2930c

File tree

3 files changed

+89
-6
lines changed

3 files changed

+89
-6
lines changed

zebra/zebra_vxlan.c

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2434,6 +2434,7 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni,
24342434
{
24352435
struct zebra_evpn *zevpn = NULL;
24362436
struct zebra_l3vni *zl3vni = NULL;
2437+
struct interface *br_if = NULL;
24372438

24382439
/* There is a possibility that VNI notification was already received
24392440
* from kernel and we programmed it as L2-VNI
@@ -2514,12 +2515,27 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni,
25142515

25152516
zevpn = zebra_evpn_add(vni);
25162517

2517-
/* Find bridge interface for the VNI */
2518-
vlan_if = zvni_map_to_svi(vnip->access_vlan,
2519-
zif->brslave_info.br_if);
2518+
/*
2519+
* Restore per-VLAN state for the newly created L2-VNI.
2520+
* This mirrors the behaviour of zebra_vxlan_if_add_vni() so
2521+
* that an L3VNI -> L2VNI transition preserves:
2522+
* - VLAN id used for the VNI (zevpn->vid)
2523+
* - associated SVI (zevpn->svi_if)
2524+
* - bridge membership (zevpn->bridge_if)
2525+
* - tenant VRF and L3VNI<-L2VNI list membership.
2526+
*/
2527+
br_if = zif->brslave_info.br_if;
2528+
zevpn_bridge_if_set(zevpn, br_if, true /* set */);
2529+
2530+
zevpn->vid = vnip->access_vlan;
2531+
2532+
vlan_if = zvni_map_to_svi(vnip->access_vlan, br_if);
25202533
if (vlan_if) {
2521-
zevpn->vrf_id = vlan_if->vrf->vrf_id;
2522-
zl3vni = zl3vni_from_vrf(vlan_if->vrf->vrf_id);
2534+
zevpn->svi_if = vlan_if;
2535+
if (vlan_if->vrf) {
2536+
zevpn->vrf_id = vlan_if->vrf->vrf_id;
2537+
zl3vni = zl3vni_from_vrf(vlan_if->vrf->vrf_id);
2538+
}
25232539
if (zl3vni)
25242540
listnode_add_sort_nodup(zl3vni->l2vnis, zevpn);
25252541
}
@@ -2532,6 +2548,12 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni,
25322548
zebra_evpn_send_add_to_client(zevpn);
25332549
zebra_evpn_read_mac_neigh(zevpn, ctx.ret_ifp);
25342550
}
2551+
2552+
if (IS_ZEBRA_DEBUG_VXLAN)
2553+
zlog_debug("L2-VNI:%d transition from L3-VNI, unlink from %s vni_info table",
2554+
vni, ctx.ret_ifp->name);
2555+
2556+
zebra_vxlan_if_vni_deref(zif, vni);
25352557
}
25362558

25372559
return 0;
@@ -5330,9 +5352,23 @@ void zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni,
53305352
br_if = vxlan_if_zif->brslave_info.br_if;
53315353
}
53325354

5333-
if (vnip)
5355+
if (vnip) {
53345356
zl3vni->vid = vnip->access_vlan;
53355357

5358+
/*
5359+
* Re-establish VLAN<->VxLAN reference for L3-VNI.
5360+
* This is needed when transitioning from L2-VNI back to
5361+
* L3-VNI (e.g., "no vni X" followed by "vni X"), as the
5362+
* reference was dropped during the L3->L2 transition by
5363+
* zebra_vxlan_if_vni_remove(). This call increments the
5364+
* reference count to maintain proper VLAN association.
5365+
* Note: zebra_evpn_vl_vxl_ref() is idempotent and safely
5366+
* handles duplicate calls for the same VNI.
5367+
*/
5368+
if (vxlan_if_zif)
5369+
zebra_evpn_vl_vxl_ref(vnip->access_vlan, vnip->vni, vxlan_if_zif);
5370+
}
5371+
53365372
if (br_if)
53375373
zl3vni_bridge_if_set(zl3vni, br_if, true);
53385374

zebra/zebra_vxlan_if.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,52 @@ static void zebra_vxlan_if_vni_iterate_callback(struct hash_bucket *bucket,
9090
ctx->func(ctx->zif, vni, ctx->arg);
9191
}
9292

93+
/*
94+
* This function only removes VLAN<->VxLAN references when transitioning
95+
* from L3-VNI to L2-VNI. It does NOT remove the VNI from the vni_table.
96+
* For actual deletion of VNI from the vni_table, use zebra_vxlan_if_vni_del()
97+
* which is called when kernel VNI configuration is removed.
98+
*/
99+
int zebra_vxlan_if_vni_deref(struct zebra_if *zif, vni_t vni)
100+
{
101+
struct zebra_vxlan_vni vni_tmp;
102+
struct zebra_vxlan_vni_info *vni_info;
103+
struct zebra_vxlan_vni *vnip;
104+
105+
/* This should be called in SVD context only */
106+
if (!IS_ZEBRA_VXLAN_IF_SVD(zif)) {
107+
if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_VXLAN)
108+
zlog_debug("%s VNI %u vxlan_if %s should be SVD, skip processing",
109+
__func__, vni, zif->ifp->name);
110+
return 0;
111+
}
112+
113+
vni_info = VNI_INFO_FROM_ZEBRA_IF(zif);
114+
memset(&vni_tmp, 0, sizeof(vni_tmp));
115+
vni_tmp.vni = vni;
116+
117+
/*
118+
* Rationale:
119+
* 1. The SVD vni_table represents kernel VNI configuration (from netlink)
120+
* and should only change when kernel config changes, not during
121+
* zebra's internal L2<->L3 VNI transitions.
122+
* 2. Keeping vnip is REQUIRED for L3-VNI re-add scenarios because:
123+
* - zl3vni_map_to_svi_if() calls zebra_vxlan_if_vni_find()
124+
* - Without vnip, SVI mapping fails -> L3-VNI stays non-operational
125+
* - access_vlan information is needed for proper state restoration
126+
* 3. This does NOT cause memory leaks because vnip remains referenced
127+
* in the hash table and will be properly freed when:
128+
* - Kernel removes the VNI (via zebra_vxlan_if_vni_del())
129+
* - Interface is deleted (via zebra_vxlan_if_del())
130+
*/
131+
vnip = (struct zebra_vxlan_vni *)hash_lookup(vni_info->vni_table, &vni_tmp);
132+
/* Decrement VLAN<->VxLAN reference count for L3-VNI removal */
133+
if (vnip)
134+
zebra_evpn_vl_vxl_deref(vnip->access_vlan, vnip->vni, zif);
135+
136+
return 0;
137+
}
138+
93139
static int zebra_vxlan_if_del_vni(struct interface *ifp,
94140
struct zebra_vxlan_vni *vnip)
95141
{

zebra/zebra_vxlan_if.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ extern int zebra_vxlan_if_down(struct interface *ifp);
6060
extern int zebra_vxlan_if_vni_up(struct interface *ifp,
6161
struct zebra_vxlan_vni *vni);
6262
extern int zebra_vxlan_if_up(struct interface *ifp);
63+
extern int zebra_vxlan_if_vni_deref(struct zebra_if *zif, vni_t vni);
6364
extern int zebra_vxlan_if_vni_del(struct interface *ifp, vni_t vni);
6465
extern int zebra_vxlan_if_del(struct interface *ifp);
6566
extern int zebra_vxlan_if_vni_table_add_update(struct interface *ifp,

0 commit comments

Comments
 (0)