First Commit of Voltha-Go-Controller from Radisys

Change-Id: I8e2e908e7ab09a4fe3d86849da18b6d69dcf4ab0
diff --git a/internal/pkg/application/vnets.go b/internal/pkg/application/vnets.go
new file mode 100644
index 0000000..cf2d1fc
--- /dev/null
+++ b/internal/pkg/application/vnets.go
@@ -0,0 +1,3138 @@
+/*
+* Copyright 2022-present Open Networking Foundation
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+ */
+
+package application
+
+import (
+	"encoding/json"
+	"errors"
+	"net"
+	infraerrorCodes "voltha-go-controller/internal/pkg/errorcodes"
+	"strconv"
+	"sync"
+	"time"
+
+	"github.com/google/gopacket"
+	"github.com/google/gopacket/layers"
+	"go.uber.org/atomic"
+
+	"voltha-go-controller/internal/pkg/controller"
+	cntlr "voltha-go-controller/internal/pkg/controller"
+	"voltha-go-controller/database"
+	"voltha-go-controller/internal/pkg/of"
+	"voltha-go-controller/internal/pkg/util"
+	errorCodes "voltha-go-controller/internal/pkg/errorcodes"
+	"github.com/opencord/voltha-lib-go/v7/pkg/log"
+)
+
+const (
+	// ICMPv6ArpGroupID constant
+	ICMPv6ArpGroupID uint32 = 1
+
+	// Radisys vendor id constant
+	Radisys string = "Radisys"
+)
+
+var (
+	//BroadcastMAC - Broadcast MAC Address
+	BroadcastMAC, _ = net.ParseMAC("ff:ff:ff:ff:ff:ff")
+)
+
+// NonZeroMacAddress utility to identify if the MAC address is non-zero.
+// We use zero MAC address as an unset MAC address
+func NonZeroMacAddress(h net.HardwareAddr) bool {
+	for i := 0; i < 6; i++ {
+		if h[i] != 0 {
+			return true
+		}
+	}
+	return false
+}
+
+// VNET package manages the different virtual networks that are part of the
+// the network. In the case of VOLT, the networks can be single tagged or
+// double tagged networks. In addition, the networks may be used for unicast
+// and multicast traffic. The unicast traffic further has two models, the
+// 1:1 and N:1 model. In case of a 1:1 model, the outer tag is same for many
+// subscribers and the inner tag is unique to each subscriber for the same
+// outer tag. The N:1 uses the same inner and outer tags, or for that matter
+// a single tag that can also be shared by subscribers. The VNET implementation
+// manages all these possibilities and the associated configuration.
+
+const (
+	// PbitMatchNone constant
+	PbitMatchNone of.PbitType = 8
+	// PbitMatchAll constant
+	PbitMatchAll of.PbitType = 0xFF
+)
+
+// SVlan        - Value of the outer tag if double tagged or the only tag if single
+//                tagged
+// SVlanTpid    - SVlan Tag Protocol Identifier
+// CVlan        - Value of the inner tag. Set to VlanNone if single tagged
+// DhcpRelay    - Set to true if the DHCP relay is enabled on the virtual network
+// MacLearning  - Set to true if the flows should include MAC address
+// UsDhcpPbit   - The pbit used for US DHCP packets
+// DsDhcpPbit   - The pbit used for DS DHCP packets
+
+// VnetConfig structure
+type VnetConfig struct {
+	Name                       string
+	SVlan                      of.VlanType
+	CVlan                      of.VlanType
+	UniVlan                    of.VlanType
+	SVlanTpid                  layers.EthernetType
+	DhcpRelay                  bool
+	ArpLearning                bool
+	MacLearning                MacLearningType
+	PppoeIa                    bool
+	ONTEtherTypeClassification int
+	VlanControl                VlanControl
+	Encapsulation              string
+	UsDhcpPbit                 []of.PbitType
+	DsDhcpPbit                 []of.PbitType
+	UsIGMPPbit                 []of.PbitType
+	DsIGMPPbit                 []of.PbitType
+	DevicesList                []string //List of serial number of devices on which this vnet is applied
+	AllowTransparent           bool
+	CtrlPktPbitRemark          map[of.PbitType]of.PbitType
+}
+
+// VnetOper structure
+type VnetOper struct {
+	PendingDeleteFlow     map[string]map[string]bool
+	DeleteInProgress      bool
+	PendingDeviceToDelete string
+	VnetLock              sync.RWMutex    `json:"-"`
+	VnetPortLock          sync.RWMutex    `json:"-"`
+	AssociatedPorts       map[string]bool `json:"-"`
+}
+
+// VoltVnet sructure
+type VoltVnet struct {
+	VnetConfig
+	VnetOper
+	Version string
+}
+
+const (
+	// EncapsulationPPPoEIA constant
+	EncapsulationPPPoEIA string = "PPPoE-IA"
+	// EncapsulationPPPoE constant
+	EncapsulationPPPoE string = "PPPoE"
+	// EncapsulationIPoE constant
+	EncapsulationIPoE string = "IPoE"
+)
+
+// NewVoltVnet is constructor for the VNET structure
+func NewVoltVnet(cfg VnetConfig) *VoltVnet {
+	var vv VoltVnet
+	vv.VnetConfig = cfg
+	if vv.PendingDeleteFlow == nil {
+		vv.PendingDeleteFlow = make(map[string]map[string]bool)
+	}
+	vv.DeleteInProgress = false
+	if cfg.Encapsulation == EncapsulationPPPoEIA {
+		vv.PppoeIa = true
+	}
+	vv.AssociatedPorts = make(map[string]bool)
+	return &vv
+}
+
+//associatePortToVnet - associate a port to Vnet
+func (vv *VoltVnet) associatePortToVnet(port string) {
+	vv.VnetPortLock.Lock()
+	if vv.AssociatedPorts == nil {
+		vv.AssociatedPorts = make(map[string]bool)
+	}
+	vv.AssociatedPorts[port] = true
+	vv.VnetPortLock.Unlock()
+}
+
+//disassociatePortFromVnet - disassociate a port from Vnet and return true if the association map is empty
+func (vv *VoltVnet) disassociatePortFromVnet(device string, port string) {
+	vv.VnetPortLock.Lock()
+	delete(vv.AssociatedPorts, port)
+	logger.Infow(ctx, "Disassociated Port from Vnet", log.Fields{"Device": device, "Port": port, "Vnet": vv.Name, "PendingDeleteFlow": vv.PendingDeleteFlow, "AssociatedPorts": vv.AssociatedPorts, "DeleteFlag": vv.DeleteInProgress})
+	vv.VnetPortLock.Unlock()
+
+	if vv.DeleteInProgress {
+		if !vv.isAssociatedPortsPresent() {
+			if len(vv.PendingDeleteFlow[device]) == 0 {
+				logger.Warnw(ctx, "Deleting Vnet", log.Fields{"Name": vv.Name})
+				GetApplication().deleteVnetConfig(vv)
+				_ = db.DelVnet(vv.Name)
+			} else {
+				logger.Warnw(ctx, "Skipping Del Vnet", log.Fields{"Name": vv.Name, "PendingDeleteFlow": vv.PendingDeleteFlow})
+			}
+		} else {
+			vv.VnetPortLock.RLock()
+			logger.Warnw(ctx, "Skipping Del Vnet", log.Fields{"Name": vv.Name, "AssociatedPorts": vv.AssociatedPorts})
+			vv.VnetPortLock.RUnlock()
+		}
+	}
+}
+
+func (vv *VoltVnet) isAssociatedPortsPresent() bool {
+	vv.VnetPortLock.RLock()
+	defer vv.VnetPortLock.RUnlock()
+	return len(vv.AssociatedPorts) != 0
+}
+
+// WriteToDb commit the VNET to the database
+func (vv *VoltVnet) WriteToDb() {
+
+	if vv.DeleteInProgress {
+		logger.Warnw(ctx, "Skipping Redis Update for Vnet, Vnet delete in progress", log.Fields{"Vnet": vv.Name})
+		return
+	}
+	vv.ForceWriteToDb()
+}
+
+//ForceWriteToDb force commit a vnet to the DB
+func (vv *VoltVnet) ForceWriteToDb() {
+	vv.VnetPortLock.RLock()
+	defer vv.VnetPortLock.RUnlock()
+	vv.Version = database.PresentVersionMap[database.VnetPath]
+	logger.Debugw(ctx, "Updating VNET....", log.Fields{"vnet": vv})
+	if b, err := json.Marshal(vv); err == nil {
+		if err:= db.PutVnet(vv.Name, string(b)); err != nil {
+			logger.Warnw(ctx, "Add Vnet to DB failed", log.Fields{"vnet name": vv.Name, "Error": err})
+		}
+	}
+}
+
+// VnetKey creates the key using the two VLAN tags
+// We append the two VLAN tags to create a single key
+func VnetKey(otag of.VlanType, itag of.VlanType, utag of.VlanType) string {
+	return strconv.Itoa(int(otag)) + "-" + strconv.Itoa(int(itag)) + "-" + strconv.Itoa(int(utag))
+}
+
+// GetVnet get VNET configuration related functionality associated with VOLT application
+func (va *VoltApplication) GetVnet(otag of.VlanType, itag of.VlanType, utag of.VlanType) *VoltVnet {
+	// When matching VNET, it is expected to match first just the outer
+	// tag, and then the combination to make sure there is no conflict
+	// for the new configuration.
+	if vnet, ok := va.VnetsByTag.Load(VnetKey(otag, of.VlanNone, utag)); ok {
+		return vnet.(*VoltVnet)
+	}
+	if vnet, ok := va.VnetsByTag.Load(VnetKey(otag, itag, utag)); ok {
+		return vnet.(*VoltVnet)
+	}
+	return nil
+}
+
+// The VNET may also be assigned name for easier references. For now,
+// the VNET is mainly identified by the two VLANs.
+
+// GetVnetByName to get vnet by name
+func (va *VoltApplication) GetVnetByName(name string) *VoltVnet {
+	if vnet, ok := va.VnetsByName.Load(name); ok {
+		return vnet.(*VoltVnet)
+	}
+	return nil
+}
+
+// storeVnetConfig to store vnet config
+func (va *VoltApplication) storeVnetConfig(cfg VnetConfig, vv *VoltVnet) {
+
+	var vnetMap *util.ConcurrentMap
+
+	va.VnetsByTag.Store(VnetKey(cfg.SVlan, cfg.CVlan, cfg.UniVlan), vv)
+	va.VnetsByName.Store(cfg.Name, vv)
+
+	if vnetMapIntf, ok := va.VnetsBySvlan.Get(vv.SVlan); !ok {
+		vnetMap = util.NewConcurrentMap()
+	} else {
+		vnetMap = vnetMapIntf.(*util.ConcurrentMap)
+	}
+	vnetMap.Set(vv, true)
+	va.VnetsBySvlan.Set(vv.SVlan, vnetMap)
+}
+
+// deleteVnetConfig to delete vnet config
+func (va *VoltApplication) deleteVnetConfig(vnet *VoltVnet) {
+	va.VnetsByTag.Delete(VnetKey(vnet.SVlan, vnet.CVlan, vnet.UniVlan))
+	va.VnetsByName.Delete(vnet.Name)
+
+	if vnetMapIntf, ok := va.VnetsBySvlan.Get(vnet.SVlan); ok {
+		vnetMap := vnetMapIntf.(*util.ConcurrentMap)
+		vnetMap.Remove(vnet)
+		va.VnetsBySvlan.Set(vnet.SVlan, vnetMap)
+	}
+}
+
+// AddVnet to add a VNET to the list of VNETs configured.
+func (va *VoltApplication) AddVnet(cfg VnetConfig, oper *VnetOper) error {
+
+	AppMutex.VnetMutex.Lock()
+	var vv *VoltVnet
+	devicesToHandle := []string{}
+	vv = va.GetVnetByName(cfg.Name)
+	if vv != nil {
+		//Could be for another OLT or could be case of backup-restore
+		for _, serialNum := range cfg.DevicesList {
+			if isDeviceInList(serialNum, vv.DevicesList) {
+				//This is backup restore scenario, just update the profile
+				logger.Info(ctx, "Add Vnet : Profile Name already exists with OLT, update-the-profile")
+				continue
+			}
+			devicesToHandle = append(devicesToHandle, serialNum)
+		}
+		if len(devicesToHandle) == 0 {
+			logger.Debug(ctx, "Ignoring Duplicate VNET by name ", log.Fields{"Vnet": cfg.Name})
+			AppMutex.VnetMutex.Unlock()
+			return nil
+		}
+	}
+
+	if vv == nil {
+		vv = NewVoltVnet(cfg)
+		if oper != nil {
+			vv.PendingDeleteFlow = oper.PendingDeleteFlow
+			vv.DeleteInProgress = oper.DeleteInProgress
+			vv.AssociatedPorts = oper.AssociatedPorts
+			vv.PendingDeviceToDelete = oper.PendingDeviceToDelete
+		}
+		devicesToHandle = append(devicesToHandle, cfg.DevicesList...)
+	} else {
+		vv.DevicesList = append(vv.DevicesList, devicesToHandle...)
+	}
+
+	va.storeVnetConfig(cfg, vv)
+	vv.WriteToDb()
+
+	logger.Infow(ctx, "Added VNET TO DB", log.Fields{"cfg": cfg, "devicesToHandle": devicesToHandle})
+
+	//va.PushDevFlowForVlan(vv)
+	AppMutex.VnetMutex.Unlock()
+	return nil
+}
+
+// DelVnet to delete a VNET from the list of VNETs configured
+func (va *VoltApplication) DelVnet(name, deviceSerialNum string) error {
+	logger.Infow(ctx, "Deleting Vnet", log.Fields{"Vnet": name})
+	AppMutex.VnetMutex.Lock()
+	if vnetIntf, ok := va.VnetsByName.Load(name); ok {
+		vnet := vnetIntf.(*VoltVnet)
+		//Delete from mvp list
+		vnet.DevicesList = util.RemoveFromSlice(vnet.DevicesList, deviceSerialNum)
+
+		va.DeleteDevFlowForVlanFromDevice(vnet, deviceSerialNum)
+		if len(vnet.DevicesList) == 0 {
+			vnet.DeleteInProgress = true
+			vnet.PendingDeviceToDelete = deviceSerialNum
+			vnet.ForceWriteToDb()
+			vnet.VnetPortLock.RLock()
+			if len(vnet.PendingDeleteFlow) == 0 && !vnet.isAssociatedPortsPresent() {
+				logger.Warnw(ctx, "Deleting Vnet", log.Fields{"Name": vnet.Name, "AssociatedPorts": vnet.AssociatedPorts, "PendingDelFlows": vnet.PendingDeleteFlow})
+				va.deleteVnetConfig(vnet)
+				_ = db.DelVnet(vnet.Name)
+			} else {
+				logger.Warnw(ctx, "Skipping Del Vnet", log.Fields{"Name": vnet.Name, "AssociatedPorts": vnet.AssociatedPorts, "PendingDelFlows": vnet.PendingDeleteFlow})
+			}
+			vnet.VnetPortLock.RUnlock()
+		} else {
+			//Update the devicelist in db
+			vnet.WriteToDb()
+		}
+	}
+	//TODO: if no vnets are present on device remove icmpv6 group from device
+	AppMutex.VnetMutex.Unlock()
+	return nil
+}
+
+// UpdateVnet to update the VNET with associated service count
+func (va *VoltApplication) UpdateVnet(vv *VoltVnet) error {
+	va.storeVnetConfig(vv.VnetConfig, vv)
+	vv.WriteToDb()
+	logger.Infow(ctx, "Updated VNET TO DB", log.Fields{"vv": vv.VnetConfig})
+	return nil
+}
+
+// ------------------------------------------------------------
+// Manifestation of a VNET on a port is handled below
+// ------------------------------------------------------------
+//
+// The VNET on a port handles everything that is done for a VNET
+// such as DHCP relay state machine, MAC addresses, IP addresses
+// learnt, so on.
+
+// DhcpStatus type
+type DhcpStatus uint8
+
+const (
+	// DhcpStatusNone constant
+	DhcpStatusNone DhcpStatus = 0
+	// DhcpStatusAcked constant
+	DhcpStatusAcked DhcpStatus = 1
+	// DhcpStatusNacked constant
+	DhcpStatusNacked DhcpStatus = 2
+	// EthTypeNone constant
+	EthTypeNone int = 0
+	// EthTypeIPoE constant
+	EthTypeIPoE int = 1
+	// EthTypePPPoE constant
+	EthTypePPPoE int = 2
+)
+
+// VoltPortVnet structure
+type VoltPortVnet struct {
+	Device                     string
+	Port                       string
+	PonPort                    uint32
+	VnetName                   string
+	SVlan                      of.VlanType
+	CVlan                      of.VlanType
+	UniVlan                    of.VlanType
+	SVlanTpid                  layers.EthernetType
+	DhcpRelay                  bool
+	ArpRelay                   bool
+	PppoeIa                    bool
+	MacLearning                MacLearningType
+	DhcpStatus                 DhcpStatus
+	DhcpExpiryTime             time.Time
+	Dhcp6ExpiryTime            time.Time
+	FlowsApplied               bool
+	services                   sync.Map
+	servicesCount              *atomic.Uint64
+	Ipv4Addr                   net.IP
+	Ipv6Addr                   net.IP
+	MacAddr                    net.HardwareAddr
+	LearntMacAddr              net.HardwareAddr
+	CircuitID                  []byte //Will not be used
+	RemoteID                   []byte //Will not be used
+	IsOption82Disabled         bool   //Will not be used
+	RelayState                 DhcpRelayState
+	PPPoeState                 PppoeIaState
+	RelayStatev6               Dhcpv6RelayState
+	IgmpEnabled                bool
+	IgmpFlowsApplied           bool
+	McastService               bool
+	ONTEtherTypeClassification int
+	VlanControl                VlanControl
+	MvlanProfileName           string
+	Version                    string
+	McastTechProfileID         uint16
+	McastPbit                  of.PbitType
+	McastUsMeterID             uint32
+	AllowTransparent           bool
+	VpvLock                    sync.Mutex `json:"-"`
+	SchedID                    int
+	DHCPv6DUID                 [MaxLenDhcpv6DUID]byte
+	PendingFlowLock            sync.RWMutex `json:"-"`
+	PendingDeleteFlow          map[string]bool
+	DeleteInProgress           bool
+	Blocked                    bool
+	DhcpPbit                   of.PbitType
+}
+
+//VlanControl vlan control type
+type VlanControl uint8
+
+const (
+	// None constant
+	// ONU and OLT will passthrough UNIVLAN as is to BNG
+	None VlanControl = iota
+
+	// ONUCVlanOLTSVlan constant
+	// Tagged traffic, ONU will replace UNIVLAN with CVLAN and OLT will add SVLAN
+	// Untagged traffic, ONU will add CVLAN and OLT will add SVLAN
+	ONUCVlanOLTSVlan
+
+	// OLTCVlanOLTSVlan constant
+	// Tagged traffic, ONU will passthrough UNIVLAN as is to OLT and
+	// OLT will replace UNIVLAN with CVLAN and add SVLAN
+	OLTCVlanOLTSVlan
+
+	// ONUCVlan constant
+	// Tagged traffic, ONU will replace UNIVLAN with CVLAN
+	// Untagged traffic, ONU will add CVLAN
+	ONUCVlan
+
+	// OLTSVlan constant
+	// UnTagged traffic, OLT will add the SVLAN
+	OLTSVlan
+)
+
+// NewVoltPortVnet is constructor for VoltPortVnet
+func NewVoltPortVnet(vnet *VoltVnet) *VoltPortVnet {
+	var vpv VoltPortVnet
+
+	vpv.VnetName = vnet.Name
+	vpv.SVlan = vnet.SVlan
+	vpv.CVlan = vnet.CVlan
+	vpv.UniVlan = vnet.UniVlan
+	vpv.SVlanTpid = vnet.SVlanTpid
+	vpv.DhcpRelay = vnet.DhcpRelay
+	vpv.DhcpStatus = DhcpStatusNone
+	vpv.PPPoeState = PppoeIaStateNone
+	vpv.ArpRelay = vnet.ArpLearning
+	vpv.PppoeIa = vnet.PppoeIa
+	vpv.VlanControl = vnet.VlanControl
+	vpv.ONTEtherTypeClassification = vnet.ONTEtherTypeClassification
+	vpv.AllowTransparent = vnet.AllowTransparent
+	vpv.FlowsApplied = false
+	vpv.IgmpEnabled = false
+	vpv.MacAddr, _ = net.ParseMAC("00:00:00:00:00:00")
+	vpv.LearntMacAddr, _ = net.ParseMAC("00:00:00:00:00:00")
+	// for OLTCVLAN SVLAN=CVLAN, UNIVLAN can differ.
+	if vpv.VlanControl == ONUCVlan {
+		vpv.CVlan = vpv.SVlan
+	}
+	// for OLTSVLAN  CVLAN=UNIVLAN , SVLAN can differ,
+	// hence assigning UNIVLAN to CVLAN, so that ONU will transparently forward the packet.
+	if vpv.VlanControl == OLTSVlan {
+		vpv.CVlan = vpv.UniVlan
+	}
+	vpv.servicesCount = atomic.NewUint64(0)
+	vpv.SchedID = 0
+	vpv.PendingDeleteFlow = make(map[string]bool)
+	vpv.DhcpPbit = vnet.UsDhcpPbit[0]
+	return &vpv
+}
+
+func (vpv *VoltPortVnet) setDevice(device string) {
+
+	if vpv.Device != device && vpv.Device != "" {
+		GetApplication().DisassociateVpvsFromDevice(device, vpv)
+		//TEMP:
+		vpv.printAssociatedVPVs(false)
+	}
+
+	logger.Infow(ctx, "Associating VPV and Device", log.Fields{"Device": vpv.Device, "Port": vpv.Port, "SVlan": vpv.SVlan})
+
+	vpv.Device = device
+	GetApplication().AssociateVpvsToDevice(device, vpv)
+	//TEMP:
+	vpv.printAssociatedVPVs(true)
+}
+
+//TODO - Nav - Temp
+func (vpv *VoltPortVnet) printAssociatedVPVs(add bool) {
+	logger.Infow(ctx, "Start----Printing all associated VPV", log.Fields{"Device": vpv.Device, "Add": add})
+	if vMap := GetApplication().GetAssociatedVpvsForDevice(vpv.Device, vpv.SVlan); vMap != nil {
+		vMap.Range(func(key, value interface{}) bool {
+			vpvEntry := key.(*VoltPortVnet)
+			logger.Infow(ctx, "Associated VPVs", log.Fields{"SVlan": vpvEntry.SVlan, "CVlan": vpvEntry.CVlan, "UniVlan": vpvEntry.UniVlan})
+			return true
+		})
+	}
+	logger.Infow(ctx, "End----Printing all associated VPV", log.Fields{"Device": vpv.Device, "Add": add})
+
+}
+
+// GetCircuitID : The interface to be satisfied by the VoltPortVnet to be a DHCP relay
+// session is implemented below. The main functions still remain in
+// the service.go file.
+func (vpv *VoltPortVnet) GetCircuitID() []byte {
+	return []byte(vpv.CircuitID)
+}
+
+// GetRemoteID to get remote id
+func (vpv *VoltPortVnet) GetRemoteID() []byte {
+	return []byte(vpv.RemoteID)
+}
+
+// GetDhcpState to get dhcp state
+func (vpv *VoltPortVnet) GetDhcpState() DhcpRelayState {
+	return vpv.RelayState
+}
+
+// SetDhcpState to set the dhcp state
+func (vpv *VoltPortVnet) SetDhcpState(state DhcpRelayState) {
+	vpv.RelayState = state
+}
+
+// GetPppoeIaState to get pppoeia state
+func (vpv *VoltPortVnet) GetPppoeIaState() PppoeIaState {
+	return vpv.PPPoeState
+}
+
+// SetPppoeIaState to set pppoeia state
+func (vpv *VoltPortVnet) SetPppoeIaState(state PppoeIaState) {
+	vpv.PPPoeState = state
+}
+
+// GetDhcpv6State to get dhcpv6 state
+func (vpv *VoltPortVnet) GetDhcpv6State() Dhcpv6RelayState {
+	return vpv.RelayStatev6
+}
+
+// SetDhcpv6State to set dhcpv6 state
+func (vpv *VoltPortVnet) SetDhcpv6State(state Dhcpv6RelayState) {
+	vpv.RelayStatev6 = state
+}
+
+// DhcpResultInd for dhcp result indication
+func (vpv *VoltPortVnet) DhcpResultInd(res *layers.DHCPv4) {
+	vpv.ProcessDhcpResult(res)
+}
+
+// Dhcpv6ResultInd for dhcpv6 result indication
+func (vpv *VoltPortVnet) Dhcpv6ResultInd(ipv6Addr net.IP, leaseTime uint32) {
+	vpv.ProcessDhcpv6Result(ipv6Addr, leaseTime)
+}
+
+// GetNniVlans to get nni vlans
+func (vpv *VoltPortVnet) GetNniVlans() (uint16, uint16) {
+	switch vpv.VlanControl {
+	case ONUCVlanOLTSVlan,
+		OLTCVlanOLTSVlan:
+		return uint16(vpv.SVlan), uint16(vpv.CVlan)
+	case ONUCVlan,
+		None:
+		return uint16(vpv.SVlan), uint16(of.VlanNone)
+	case OLTSVlan:
+		return uint16(vpv.SVlan), uint16(of.VlanNone)
+	}
+	return uint16(of.VlanNone), uint16(of.VlanNone)
+}
+
+// GetService to get service
+func (vpv *VoltPortVnet) GetService(name string) (*VoltService, bool) {
+	service, ok := vpv.services.Load(name)
+	if ok {
+		return service.(*VoltService), ok
+	}
+	return nil, ok
+}
+
+// AddService to add service
+func (vpv *VoltPortVnet) AddService(service *VoltService) {
+	vpv.services.Store(service.Name, service)
+	vpv.servicesCount.Inc()
+	logger.Infow(ctx, "Service added/updated to VPV", log.Fields{"Port": vpv.Port, "SVLAN": vpv.SVlan, "CVLAN": vpv.CVlan, "UNIVlan": vpv.UniVlan, "Service": service.Name, "Count": vpv.servicesCount.Load()})
+}
+
+// DelService to delete service
+func (vpv *VoltPortVnet) DelService(service *VoltService) {
+	vpv.services.Delete(service.Name)
+	vpv.servicesCount.Dec()
+
+	// If the only Igmp Enabled service is removed, remove the Igmp trap flow along with it
+	if service.IgmpEnabled {
+		if err := vpv.DelIgmpFlows(); err != nil {
+			statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+			vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+		}
+
+		vpv.IgmpEnabled = false
+	}
+	logger.Infow(ctx, "Service deleted from VPV", log.Fields{"Port": vpv.Port, "SVLAN": vpv.SVlan, "CVLAN": vpv.CVlan, "UNIVlan": vpv.UniVlan, "Service": service.Name, "Count": vpv.servicesCount.Load()})
+}
+
+// ProcessDhcpResult to process dhcp results
+func (vpv *VoltPortVnet) ProcessDhcpResult(res *layers.DHCPv4) {
+	msgType := DhcpMsgType(res)
+	if msgType == layers.DHCPMsgTypeAck {
+		vpv.ProcessDhcpSuccess(res)
+	} else if msgType == layers.DHCPMsgTypeNak {
+		vpv.DhcpStatus = DhcpStatusNacked
+	}
+	vpv.WriteToDb()
+}
+
+// ProcessDhcpSuccess : Learn the IPv4 address allocated to the services and update the
+// the services with the same. This also calls for adding flows
+// for the services as the DHCP procedure is completed
+func (vpv *VoltPortVnet) ProcessDhcpSuccess(res *layers.DHCPv4) {
+	vpv.DhcpStatus = DhcpStatusAcked
+	vpv.Ipv4Addr, _ = GetIpv4Addr(res)
+	logger.Infow(ctx, "Received IPv4 Address", log.Fields{"IP Address": vpv.Ipv4Addr.String()})
+	logger.Infow(ctx, "Services Configured", log.Fields{"Count": vpv.servicesCount.Load()})
+
+	vpv.services.Range(vpv.updateIPv4AndProvisionFlows)
+	vpv.ProcessDhcpv4Options(res)
+}
+
+// ProcessDhcpv4Options : Currently we process lease time and store the validity of the
+// IP address allocated.
+func (vpv *VoltPortVnet) ProcessDhcpv4Options(res *layers.DHCPv4) {
+	for _, o := range res.Options {
+		switch o.Type {
+		case layers.DHCPOptLeaseTime:
+			leasetime := GetIPv4LeaseTime(o)
+			vpv.DhcpExpiryTime = time.Now().Add((time.Duration(leasetime) * time.Second))
+			logger.Infow(ctx, "Lease Expiry Set", log.Fields{"Time": vpv.DhcpExpiryTime})
+		}
+	}
+}
+
+// ProcessDhcpv6Result : Read the IPv6 address allocated to the device and store it on the
+// VNET. The same IPv6 address is also passed to the services. When a
+// service is fetched all the associated information such as MAC address,
+// IPv4 address and IPv6 addresses can be provided.
+func (vpv *VoltPortVnet) ProcessDhcpv6Result(ipv6Addr net.IP, leaseTime uint32) {
+	// TODO: Status based hanlding of flows
+	vpv.Dhcp6ExpiryTime = time.Now().Add((time.Duration(leaseTime) * time.Second))
+	vpv.Ipv6Addr = ipv6Addr
+
+	vpv.services.Range(vpv.updateIPv6AndProvisionFlows)
+	vpv.WriteToDb()
+}
+
+// AddSvcUsMeterToDevice to add service upstream meter info to device
+func AddSvcUsMeterToDevice(key, value interface{}) bool {
+	svc := value.(*VoltService)
+	logger.Info(ctx, "Adding upstream meter profile to device", log.Fields{"ServiceName": svc.Name})
+	if device, _ := GetApplication().GetDeviceFromPort(svc.Port); device != nil {
+		GetApplication().AddMeterToDevice(svc.Port, device.Name, svc.UsMeterID, 0)
+		return true
+	}
+	logger.Errorw(ctx, "Dropping US Meter request: Device not found", log.Fields{"Service": svc})
+	return false
+}
+
+// PushFlowsForPortVnet - triggers flow construction and push for provided VPV
+func (vpv *VoltPortVnet) PushFlowsForPortVnet(d *VoltDevice) {
+
+	vp := d.GetPort(vpv.Port)
+
+	//Ignore if UNI port is not found or not UP
+	if vp == nil || vp.State != PortStateUp {
+		logger.Warnw(ctx, "Ignoring Vlan UP Ind for VPV: Port Not Found/Ready", log.Fields{"Port": vp})
+		return
+	}
+
+	if vpv.PonPort != 0xFF && vpv.PonPort != vp.PonPort {
+		logger.Errorw(ctx, "UNI port discovered on wrong PON Port. Dropping Flow Configuration for VPV", log.Fields{"Device": d.Name, "Port": vpv.Port, "DetectedPon": vp.PonPort, "ExpectedPon": vpv.PonPort, "Vnet": vpv.VnetName})
+		return
+	}
+
+	//Disable the flag so that flows can be pushed again
+	// vpv.IgmpFlowsApplied = false
+	// vpv.DsFlowsApplied = false
+	// vpv.UsFlowsApplied = false
+	vpv.VpvLock.Lock()
+	vpv.PortUpInd(d, vpv.Port)
+	vpv.VpvLock.Unlock()
+}
+
+// PortUpInd : When a port transistions to UP state, the indication is passed
+// on to this module via the application. We read the VNET configuration
+// again here to apply the latest configuration if the configuration
+// changed. Thus, a reboot of ONT forces the new configuration to get
+// applied.
+func (vpv *VoltPortVnet) PortUpInd(device *VoltDevice, port string) {
+
+	if vpv.DeleteInProgress {
+		logger.Errorw(ctx, "Ignoring VPV Port UP Ind, VPV deleteion In-Progress", log.Fields{"Device": device, "Port": port, "Vnet": vpv.VnetName})
+		return
+	}
+	vpv.setDevice(device.Name)
+	logger.Infow(ctx, "Port UP Ind, pushing flows for the port", log.Fields{"Device": device, "Port": port, "VnetDhcp": vpv.DhcpRelay, "McastService": vpv.McastService})
+
+	nni, _ := GetApplication().GetNniPort(device.Name)
+	if nni == "" {
+		logger.Warnw(ctx, "Ignoring Vnet Port UP indication: NNI is unavailable", log.Fields{"Port": vpv.Port, "Device": device.Name})
+		return
+	}
+
+	if vp := device.GetPort(port); vp != nil {
+
+		if vpv.PonPort != 0xFF && vpv.PonPort != vp.PonPort {
+			logger.Errorw(ctx, "UNI port discovered on wrong PON Port. Dropping Flow Config for VPV", log.Fields{"Device": device.Name, "Port": port, "DetectedPon": vp.PonPort, "ExpectedPon": vpv.PonPort, "Vnet": vpv.VnetName})
+			return
+		}
+	}
+
+	if vpv.Blocked {
+		logger.Errorw(ctx, "VPV Bocked for Processing. Ignoring flow push request", log.Fields{"Port": vpv.Port, "Vnet": vpv.VnetName})
+		return
+	}
+
+	if vpv.DhcpRelay || vpv.ArpRelay || vpv.PppoeIa {
+		// If MAC Learning is true if no MAC is configured, push DS/US DHCP, US HSIA flows without MAC.
+		// DS HSIA flows are installed after learning the MAC.
+		logger.Infow(ctx, "Port Up - Trap Flows", log.Fields{"Device": device.Name, "Port": port})
+		// no HSIA flows for multicast service
+		if !vpv.McastService {
+			vpv.services.Range(AddUsHsiaFlows)
+		}
+		vpv.AddTrapFlows()
+		if vpv.MacLearning == MacLearningNone || NonZeroMacAddress(vpv.MacAddr) {
+			logger.Infow(ctx, "Port Up - DS Flows", log.Fields{"Device": device.Name, "Port": port})
+			// US & DS DHCP, US HSIA flows are already installed
+			// install only DS HSIA flow here.
+			// no HSIA flows for multicast service
+			if !vpv.McastService {
+				vpv.services.Range(AddDsHsiaFlows)
+			}
+		}
+
+	} else {
+		// DHCP relay is not configured. This implies that the service must use
+		// 1:1 and does not require MAC learning. In a completely uncommon but
+		// plausible case, the MAC address can be learnt from N:1 without DHCP
+		// relay by configuring any unknown MAC address to be reported. This
+		// however is not seen as a real use case.
+		logger.Infow(ctx, "Port Up - Service Flows", log.Fields{"Device": device.Name, "Port": port})
+		if !vpv.McastService {
+			vpv.services.Range(AddUsHsiaFlows)
+		}
+		vpv.AddTrapFlows()
+		if !vpv.McastService {
+			vpv.services.Range(AddDsHsiaFlows)
+		}
+	}
+
+	// Process IGMP proxy - install IGMP trap rules before DHCP trap rules
+	if vpv.IgmpEnabled {
+		logger.Infow(ctx, "Port Up - IGMP Flows", log.Fields{"Device": device.Name, "Port": port})
+		vpv.services.Range(AddSvcUsMeterToDevice)
+		if err := vpv.AddIgmpFlows(); err != nil {
+			statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+			vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+		}
+
+		if vpv.McastService {
+			vpv.services.Range(PostAccessConfigSuccessInd)
+		}
+	}
+
+	vpv.WriteToDb()
+}
+
+// PortDownInd : When the port status changes to down, we delete all configured flows
+// The same indication is also passed to the services enqueued for them
+// to take appropriate actions
+func (vpv *VoltPortVnet) PortDownInd(device string, port string) {
+
+	logger.Infow(ctx, "VPV Port DOWN Ind, deleting all flows for services",
+		log.Fields{"service count": vpv.servicesCount.Load()})
+
+	//vpv.services.Range(DelAllFlows)
+	vpv.DelTrapFlows()
+	vpv.DelHsiaFlows()
+	vpv.WriteToDb()
+	vpv.ClearServiceCounters()
+}
+
+// SetMacAddr : The MAC address is set when a MAC address is learnt through the
+// packets received from the network. Currently, DHCP packets are
+// only packets we learn the MAC address from
+func (vpv *VoltPortVnet) SetMacAddr(addr net.HardwareAddr) {
+
+	//Store Learnt MAC address and return if MACLearning is not enabled
+	vpv.LearntMacAddr = addr
+	if vpv.MacLearning == MacLearningNone || !NonZeroMacAddress(addr) ||
+		(NonZeroMacAddress(vpv.MacAddr) && vpv.MacLearning == Learn) {
+		return
+	}
+
+	// Compare the two MAC addresses to see if it is same
+	// If they are same, we just return. If not, we perform
+	// actions to address the change in MAC address
+	//if NonZeroMacAddress(vpv.MacAddr) && !util.MacAddrsMatch(vpv.MacAddr, addr) {
+	if !util.MacAddrsMatch(vpv.MacAddr, addr) {
+		expectedPort := GetApplication().GetMacInPortMap(addr)
+		if expectedPort != "" && expectedPort != vpv.Port {
+			logger.Errorw(ctx, "mac-learnt-from-different-port-ignoring-setmacaddr",
+				log.Fields{"ExpectedPort": expectedPort, "ReceivedPort": vpv.Port, "LearntMacAdrr": vpv.MacAddr, "NewMacAdrr": addr.String()})
+			return
+		}
+		if NonZeroMacAddress(vpv.MacAddr) {
+			logger.Warnw(ctx, "MAC Address Changed. Remove old flows (if added) and re-add with updated MAC", log.Fields{"UpdatedMAC": addr})
+
+			// The newly learnt MAC address is different than earlier one.
+			// The existing MAC based HSIA flows need to be undone as the device
+			// may have been changed
+			// Atleast one HSIA flow should be present in adapter to retain the TP and GEM
+			// hence delete one after the other
+			vpv.services.Range(DelUsHsiaFlows)
+			vpv.MacAddr = addr
+			vpv.services.Range(vpv.setLearntMAC)
+			vpv.services.Range(AddUsHsiaFlows)
+			vpv.services.Range(DelDsHsiaFlows)
+			GetApplication().DeleteMacInPortMap(vpv.MacAddr)
+		} else {
+			vpv.MacAddr = addr
+			vpv.services.Range(vpv.setLearntMAC)
+			logger.Infow(ctx, "MAC Address learnt from DHCP or ARP", log.Fields{"Learnt MAC": addr.String(), "Port": vpv.Port})
+		}
+		GetApplication().UpdateMacInPortMap(vpv.MacAddr, vpv.Port)
+	} else {
+		logger.Infow(ctx, "Leant MAC Address is same", log.Fields{"Learnt MAC": addr.String(), "Port": vpv.Port})
+	}
+
+	_, err := GetApplication().GetDeviceFromPort(vpv.Port)
+	if err != nil {
+		logger.Warnw(ctx, "Not pushing Service Flows: Error Getting Device", log.Fields{"Reason": err.Error()})
+		statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+		vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+		return
+	}
+	// Ds Hsia flows has to be pushed
+	if vpv.FlowsApplied {
+		// no HSIA flows for multicast service
+		if !vpv.McastService {
+			vpv.services.Range(AddDsHsiaFlows)
+		}
+	}
+	vpv.WriteToDb()
+}
+
+// MatchesVlans : If the VNET matches both S and C VLANs, return true. Else, return false
+func (vpv *VoltPortVnet) MatchesVlans(svlan of.VlanType, cvlan of.VlanType, univlan of.VlanType) bool {
+	if vpv.SVlan != svlan || vpv.CVlan != cvlan || vpv.UniVlan != univlan {
+		return false
+	}
+	return true
+}
+
+// MatchesCvlan : If the VNET matches CVLAN, return true. Else, return false
+func (vpv *VoltPortVnet) MatchesCvlan(cvlan []of.VlanType) bool {
+	if len(cvlan) != 1 && !vpv.AllowTransparent {
+		return false
+	}
+	if vpv.CVlan != cvlan[0] {
+		return false
+	}
+	return true
+}
+
+// MatchesPriority : If the VNET matches priority of the incoming packet with any service, return true. Else, return false
+func (vpv *VoltPortVnet) MatchesPriority(priority uint8) *VoltService {
+
+	var service *VoltService
+	pbitFound := false
+	matchpbitsFunc := func(key, value interface{}) bool {
+		svc := value.(*VoltService)
+		for _, pbit := range svc.Pbits {
+			if uint8(pbit) == priority {
+				logger.Infow(ctx, "Pbit match found with service",
+					log.Fields{"Pbit": priority, "serviceName": svc.Name})
+				pbitFound = true
+				service = svc
+				return false //Returning false to stop the Range loop
+			}
+		}
+		return true
+	}
+	_ = pbitFound
+	vpv.services.Range(matchpbitsFunc)
+	return service
+}
+
+// GetRemarkedPriority : If the VNET matches priority of the incoming packet with any service, return true. Else, return false
+func (vpv *VoltPortVnet) GetRemarkedPriority(priority uint8) uint8 {
+
+	dsPbit := uint8(0)
+	matchpbitsFunc := func(key, value interface{}) bool {
+		svc := value.(*VoltService)
+		if remarkPbit, ok := svc.DsRemarkPbitsMap[int(priority)]; ok {
+			logger.Infow(ctx, "Pbit match found with service",
+				log.Fields{"Pbit": priority, "serviceName": svc.Name, "remarkPbit": remarkPbit})
+			dsPbit = uint8(remarkPbit)
+			return false //Returning false to stop the Range loop
+		}
+		// When no remarking info is available, remark the incoming pbit
+		// to highest pbit configured for the subscriber (across all subservices associated)
+		svcPbit := uint8(svc.Pbits[0])
+		if svcPbit > dsPbit {
+			dsPbit = svcPbit
+		}
+		return true
+	}
+	vpv.services.Range(matchpbitsFunc)
+	logger.Debugw(ctx, "Remarked Pbit Value", log.Fields{"Incoming": priority, "Remarked": dsPbit})
+	return dsPbit
+}
+
+// AddSvc adds a service on the VNET on a port. The addition is
+// triggered when NB requests for service addition
+func (vpv *VoltPortVnet) AddSvc(svc *VoltService) {
+
+	//vpv.services = append(vpv.services, svc)
+	vpv.AddService(svc)
+	logger.Debugw(ctx, "Added Service to VPV", log.Fields{"Num of SVCs": vpv.servicesCount.Load(), "SVC": svc})
+
+	// Learn the circuit-id and remote-id from the service
+	// TODO: There must be a better way of doing this. This
+	// may be explored
+	if svc.IgmpEnabled {
+		vpv.IgmpEnabled = true
+	}
+	// first time service activation MacLearning will have default value as None.
+	// to handle reciliency if anythng other then None we should retain it .
+	if svc.MacLearning == MacLearningNone {
+		if !vpv.DhcpRelay && !vpv.ArpRelay {
+			svc.MacLearning = MacLearningNone
+		} else if vpv.MacLearning == Learn {
+			svc.MacLearning = Learn
+		} else if vpv.MacLearning == ReLearn {
+			svc.MacLearning = ReLearn
+		}
+	}
+
+	//TODO: Temp Change - Need to address MAC Learning flow issues completely
+	if (svc.MacLearning == Learn || svc.MacLearning == ReLearn) && NonZeroMacAddress(vpv.MacAddr) {
+		svc.MacAddr = vpv.MacAddr
+	} else if vpv.servicesCount.Load() == 1 {
+		vpv.MacAddr = svc.MacAddr
+	}
+
+	vpv.MacLearning = svc.MacLearning
+	vpv.PonPort = svc.PonPort
+	logger.Debugw(ctx, "Added MAC to VPV", log.Fields{"MacLearning": vpv.MacLearning, "VPV": vpv})
+	//Reconfigure Vlans based on Vlan Control type
+	svc.VlanControl = vpv.VlanControl
+	// for OLTCVLAN SVLAN=CVLAN, UNIVLAN can differ.
+	if vpv.VlanControl == ONUCVlan {
+		svc.CVlan = svc.SVlan
+	}
+	// for OLTSVLAN  CVLAN=UNIVLAN , SVLAN can differ,
+	// hence assigning UNIVLAN to CVLAN, so that ONU will transparently forward the packet.
+	if vpv.VlanControl == OLTSVlan {
+		svc.CVlan = svc.UniVlan
+	}
+	if svc.McastService {
+		vpv.McastService = true
+		vpv.McastTechProfileID = svc.TechProfileID
+		//Assumption: Only one Pbit for mcast service
+		vpv.McastPbit = svc.Pbits[0]
+		vpv.McastUsMeterID = svc.UsMeterID
+		vpv.SchedID = svc.SchedID
+	}
+	svc.ONTEtherTypeClassification = vpv.ONTEtherTypeClassification
+	svc.AllowTransparent = vpv.AllowTransparent
+	svc.SVlanTpid = vpv.SVlanTpid
+
+	//Ensure configuring the mvlan profile only once
+	//One subscriber cannot have multiple mvlan profiles. Only the first configuration is valid
+	if svc.MvlanProfileName != "" {
+		if vpv.MvlanProfileName == "" {
+			vpv.MvlanProfileName = svc.MvlanProfileName
+		} else {
+			logger.Warnw(ctx, "Mvlan Profile already configured for subscriber. Ignoring new Mvlan", log.Fields{"Existing Mvlan": vpv.MvlanProfileName, "New Mvlan": svc.MvlanProfileName})
+		}
+	}
+
+	_, err := GetApplication().GetDeviceFromPort(vpv.Port)
+	if err != nil {
+		logger.Warnw(ctx, "Not pushing Service Flows: Error Getting Device", log.Fields{"Reason": err.Error()})
+		//statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+		//TODO-COMM: 		vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+		return
+	}
+
+	//Push Service Flows if DHCP relay is not configured
+	//or already DHCP flows are configured for the VPV
+	//to which the serivce is associated
+	if vpv.FlowsApplied {
+		if NonZeroMacAddress(vpv.MacAddr) || svc.MacLearning == MacLearningNone {
+			svc.AddHsiaFlows()
+		} else {
+			if err:= svc.AddUsHsiaFlows(); err != nil {
+				logger.Warnw(ctx, "Add US hsia flow failed", log.Fields{"service": svc.Name, "Error": err})
+			}
+		}
+	}
+
+	//Assumption: Igmp will be enabled only for one service and SubMgr ensure the same
+	// When already the port is UP and provisioned a service without igmp, then trap flows for subsequent
+	// service with Igmp Enabled needs to be installed
+	if svc.IgmpEnabled && vpv.FlowsApplied {
+		logger.Infow(ctx, "Add Service - IGMP Flows", log.Fields{"Device": vpv.Device, "Port": vpv.Port})
+		if err := vpv.AddIgmpFlows(); err != nil {
+			statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+			vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+		}
+
+		if vpv.McastService {
+			//For McastService, send Service Activated indication once IGMP US flow is pushed
+			vpv.services.Range(PostAccessConfigSuccessInd)
+		}
+	}
+	vpv.WriteToDb()
+}
+
+// setLearntMAC to set learnt mac
+func (vpv *VoltPortVnet) setLearntMAC(key, value interface{}) bool {
+	svc := value.(*VoltService)
+	svc.SetMacAddr(vpv.MacAddr)
+	svc.WriteToDb()
+	return true
+}
+
+// PostAccessConfigSuccessInd for posting access config success indication
+func PostAccessConfigSuccessInd(key, value interface{}) bool {
+	return true
+}
+
+// updateIPv4AndProvisionFlows to update ipv4 and provisional flows
+func (vpv *VoltPortVnet) updateIPv4AndProvisionFlows(key, value interface{}) bool {
+	svc := value.(*VoltService)
+	logger.Info(ctx, "Updating Ipv4 address for service", log.Fields{"ServiceName": svc.Name})
+	svc.SetIpv4Addr(vpv.Ipv4Addr)
+	svc.WriteToDb()
+
+	return true
+}
+
+// updateIPv6AndProvisionFlows to update ipv6 and provisional flow
+func (vpv *VoltPortVnet) updateIPv6AndProvisionFlows(key, value interface{}) bool {
+	svc := value.(*VoltService)
+	svc.SetIpv6Addr(vpv.Ipv6Addr)
+	svc.WriteToDb()
+
+	return true
+}
+
+// AddUsHsiaFlows to add upstream hsia flows
+func AddUsHsiaFlows(key, value interface{}) bool {
+	svc := value.(*VoltService)
+	if err:= svc.AddUsHsiaFlows(); err != nil {
+		logger.Warnw(ctx, "Add US hsia flow failed", log.Fields{"service": svc.Name, "Error": err})
+	}
+	return true
+}
+
+// AddDsHsiaFlows to add downstream hsia flows
+func AddDsHsiaFlows(key, value interface{}) bool {
+	svc := value.(*VoltService)
+	if err:= svc.AddDsHsiaFlows(); err != nil {
+		logger.Warnw(ctx, "Add DS hsia flow failed", log.Fields{"service": svc.Name, "Error": err})
+	}
+	return true
+}
+
+// ClearFlagsInService to clear the flags used in service
+func ClearFlagsInService(key, value interface{}) bool {
+	svc := value.(*VoltService)
+	svc.ServiceLock.Lock()
+	svc.IgmpFlowsApplied = false
+	svc.DsDhcpFlowsApplied = false
+	svc.DsHSIAFlowsApplied = false
+	svc.Icmpv6FlowsApplied = false
+	svc.UsHSIAFlowsApplied = false
+	svc.UsDhcpFlowsApplied = false
+	svc.PendingFlows = make(map[string]bool)
+	svc.AssociatedFlows = make(map[string]bool)
+	svc.ServiceLock.Unlock()
+	svc.WriteToDb()
+	logger.Debugw(ctx, "Cleared Flow Flags for service", log.Fields{"name": svc.Name})
+	return true
+}
+
+// DelDsHsiaFlows to delete hsia flows
+func DelDsHsiaFlows(key, value interface{}) bool {
+	svc := value.(*VoltService)
+	if err:= svc.DelDsHsiaFlows(); err != nil {
+		logger.Warnw(ctx, "Delete DS hsia flow failed", log.Fields{"service": svc.Name, "Error": err})
+	}
+	return true
+}
+
+// DelUsHsiaFlows to delete upstream hsia flows
+func DelUsHsiaFlows(key, value interface{}) bool {
+	svc := value.(*VoltService)
+	if err:= svc.DelUsHsiaFlows(); err != nil {
+		logger.Warnw(ctx, "Delete US hsia flow failed", log.Fields{"service": svc.Name, "Error": err})
+	}
+	return true
+}
+
+// ClearServiceCounters to clear the service counters
+func ClearServiceCounters(key, value interface{}) bool {
+	svc := value.(*VoltService)
+	//Delete the per service counter too
+	GetApplication().ServiceCounters.Delete(svc.Name)
+	if svc.IgmpEnabled && svc.EnableMulticastKPI {
+		_ = db.DelAllServiceChannelCounter(svc.Name)
+	}
+	return true
+}
+
+//AddTrapFlows - Adds US & DS Trap flows
+func (vpv *VoltPortVnet) AddTrapFlows() {
+
+	if !vpv.FlowsApplied || vgcRebooted {
+		if vpv.DhcpRelay {
+			if err := vpv.AddUsDhcpFlows(); err != nil {
+				statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+				vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+			}
+			if err := vpv.AddDsDhcpFlows(); err != nil {
+				statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+				vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+			}
+			logger.Infow(ctx, "ICMPv6 MC Group modification will not be triggered to rwcore for ",
+				log.Fields{"port": vpv.Port})
+			//vpv.updateICMPv6McGroup(true)
+		} else if vpv.ArpRelay {
+			if err := vpv.AddUsArpFlows(); err != nil {
+				statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+				vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+			}
+			logger.Info(ctx, "ARP trap rules not added in downstream direction")
+
+		} else if vpv.PppoeIa {
+			if err := vpv.AddUsPppoeFlows(); err != nil {
+				statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+				vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+			}
+			if err := vpv.AddDsPppoeFlows(); err != nil {
+				statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+				vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+			}
+		}
+		vpv.FlowsApplied = true
+		vpv.WriteToDb()
+	}
+}
+
+//DelTrapFlows - Removes all US & DS DHCP, IGMP trap flows.
+func (vpv *VoltPortVnet) DelTrapFlows() {
+
+	// Delete HSIA & DHCP flows before deleting IGMP flows
+	if vpv.FlowsApplied || vgcRebooted {
+		if vpv.DhcpRelay {
+			if err:= vpv.DelUsDhcpFlows(); err != nil {
+				logger.Warnw(ctx, "Delete US hsia flow failed", log.Fields{"port": vpv.Port, "SVlan": vpv.SVlan, "CVlan": vpv.CVlan,
+					"UniVlan": vpv.UniVlan, "Error": err})
+			}
+			logger.Infow(ctx, "ICMPv6 MC Group modification will not be triggered to rwcore  for ",
+				log.Fields{"port": vpv.Port})
+			if err := vpv.DelDsDhcpFlows(); err != nil {
+				statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+				vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+			}
+			//vpv.updateICMPv6McGroup(false)
+		} else if vpv.ArpRelay {
+			if err := vpv.DelUsArpFlows(); err != nil {
+				statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+				vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+			}
+		} else if vpv.PppoeIa {
+			if err := vpv.DelUsPppoeFlows(); err != nil {
+				statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+				vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+			}
+			if err := vpv.DelDsPppoeFlows(); err != nil {
+				statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+				vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+			}
+		}
+		vpv.FlowsApplied = false
+		vpv.WriteToDb()
+	}
+	if err:= vpv.DelIgmpFlows(); err != nil {
+		logger.Warnw(ctx, "Delete igmp flow failed", log.Fields{"port": vpv.Port, "SVlan": vpv.SVlan, "CVlan": vpv.CVlan,
+			"UniVlan": vpv.UniVlan, "Error": err})
+	}
+}
+
+// DelHsiaFlows deletes the service flows
+func (vpv *VoltPortVnet) DelHsiaFlows() {
+	// no HSIA flows for multicast service
+	if !vpv.McastService {
+		vpv.services.Range(DelUsHsiaFlows)
+		vpv.services.Range(DelDsHsiaFlows)
+	}
+}
+
+//ClearServiceCounters - Removes all igmp counters for a service
+func (vpv *VoltPortVnet) ClearServiceCounters() {
+	//send flows deleted indication to submgr
+	vpv.services.Range(ClearServiceCounters)
+}
+
+// AddUsDhcpFlows pushes the DHCP flows to the VOLTHA via the controller
+func (vpv *VoltPortVnet) AddUsDhcpFlows() error {
+	var vd *VoltDevice
+	device := vpv.Device
+
+	if vd = GetApplication().GetDevice(device); vd != nil {
+		if vd.State != controller.DeviceStateUP {
+			logger.Errorw(ctx, "Skipping US DHCP Flow Push - Device state DOWN", log.Fields{"Port": vpv.Port, "SVLAN": vpv.SVlan, "CVLAN": vpv.CVlan, "UNIVlan": vpv.UniVlan, "device": device})
+			return nil
+		}
+	} else {
+		logger.Errorw(ctx, "US DHCP Flow Push Failed- Device not found", log.Fields{"Port": vpv.Port, "SVLAN": vpv.SVlan, "CVLAN": vpv.CVlan, "UNIVlan": vpv.UniVlan, "device": device})
+		return errorCodes.ErrDeviceNotFound
+	}
+
+	flows, err := vpv.BuildUsDhcpFlows()
+	if err == nil {
+		logger.Debugw(ctx, "Adding US DHCP flows", log.Fields{"Device": device})
+		if err1 := vpv.PushFlows(vd, flows); err1 != nil {
+			//push ind here ABHI
+			statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err1)
+			vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+		}
+	} else {
+		logger.Errorw(ctx, "US DHCP Flow Add Failed", log.Fields{"Reason": err.Error(), "Device": device})
+		//push ind here ABHI
+		statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+		vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+
+	}
+	/*
+	flows, err = vpv.BuildUsDhcp6Flows()
+	if err == nil {
+		logger.Debugw(ctx, "Adding US DHCP6 flows", log.Fields{"Device": device})
+		if err1 := vpv.PushFlows(vd, flows); err1 != nil {
+			//pussh ind here ABHI
+			statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err1)
+			vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+
+		}
+	} else {
+		logger.Errorw(ctx, "US DHCP6 Flow Add Failed", log.Fields{"Reason": err.Error(), "Device": device})
+		//push ind here ABHI
+		statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+		vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+
+	}*/
+	return nil
+}
+
+// AddDsDhcpFlows function pushes the DHCP flows to the VOLTHA via the controller
+func (vpv *VoltPortVnet) AddDsDhcpFlows() error {
+
+	var vd *VoltDevice
+	device := vpv.Device
+
+	if vd = GetApplication().GetDevice(device); vd != nil {
+		if vd.State != controller.DeviceStateUP {
+			logger.Errorw(ctx, "Skipping DS DHCP Flow Push - Device state DOWN", log.Fields{"Port": vpv.Port, "SVLAN": vpv.SVlan, "CVLAN": vpv.CVlan, "UNIVlan": vpv.UniVlan, "device": device})
+			return nil
+		}
+	} else {
+		logger.Errorw(ctx, "DS DHCP Flow Push Failed- Device not found", log.Fields{"Port": vpv.Port, "SVLAN": vpv.SVlan, "CVLAN": vpv.CVlan, "UNIVlan": vpv.UniVlan, "device": device})
+		return errorCodes.ErrDeviceNotFound
+	}
+	if GetApplication().GetVendorID() != Radisys && vd.GlobalDhcpFlowAdded {
+		return nil
+	}
+
+	flows, err := vpv.BuildDsDhcpFlows()
+	if err == nil {
+		if err1 := vpv.PushFlows(vd, flows); err1 != nil {
+			//push ind here and procced
+			statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err1)
+			vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+
+		}
+	} else {
+		logger.Errorw(ctx, "DS DHCP Flow Add Failed", log.Fields{"Reason": err.Error()})
+		//send ind here and proceed
+		statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+		vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+
+	}
+	/*
+	flows, err = vpv.BuildDsDhcp6Flows()
+	if err == nil {
+		if err1 := vpv.PushFlows(vd, flows); err1 != nil {
+			//push ind and proceed
+			statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err1)
+			vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+
+		}
+	} else {
+		logger.Errorw(ctx, "DS DHCP6 Flow Add Failed", log.Fields{"Reason": err.Error()})
+		//Send ind here and proceed
+		statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+		vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+
+	}*/
+	if GetApplication().GetVendorID() != Radisys {
+		vd.GlobalDhcpFlowAdded = true
+	}
+	return nil
+}
+
+// DelDhcpFlows deletes both US & DS DHCP flows applied for this Vnet instantiated on the port
+func (vpv *VoltPortVnet) DelDhcpFlows() {
+	if err := vpv.DelUsDhcpFlows(); err != nil {
+		statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+		vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+	}
+
+	if err := vpv.DelDsDhcpFlows(); err != nil {
+		statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+		vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+	}
+}
+
+// DelUsDhcpFlows delete the DHCP flows applied for this Vnet instantiated on the port
+// Write the status of the VPV to the DB once the delete is scheduled
+// for dispatch
+func (vpv *VoltPortVnet) DelUsDhcpFlows() error {
+	device, err := GetApplication().GetDeviceFromPort(vpv.Port)
+	if err != nil {
+		return err
+	}
+
+	err = vpv.delDhcp4Flows(device)
+	if err != nil {
+		statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+		vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+	}
+	/*
+	err = vpv.delDhcp6Flows(device)
+	if err != nil {
+		statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+		vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+	}*/
+	return nil
+}
+
+func (vpv *VoltPortVnet) delDhcp4Flows(device *VoltDevice) error {
+	flows, err := vpv.BuildUsDhcpFlows()
+	if err == nil {
+		return vpv.RemoveFlows(device, flows)
+	}
+	logger.Errorw(ctx, "US DHCP Flow Delete Failed", log.Fields{"Reason": err.Error()})
+	return err
+}
+/*
+func (vpv *VoltPortVnet) delDhcp6Flows(device *VoltDevice) error {
+	flows, err := vpv.BuildUsDhcp6Flows()
+	if err == nil {
+		return vpv.RemoveFlows(device, flows)
+	}
+	logger.Errorw(ctx, "US DHCP6 Flow Delete Failed", log.Fields{"Reason": err.Error()})
+	return err
+
+}*/
+
+// DelDsDhcpFlows delete the DHCP flows applied for this Vnet instantiated on the port
+// Write the status of the VPV to the DB once the delete is scheduled
+// for dispatch
+func (vpv *VoltPortVnet) DelDsDhcpFlows() error {
+	device, err := GetApplication().GetDeviceFromPort(vpv.Port)
+	if err != nil {
+		return err
+	}
+	err = vpv.delDsDhcp4Flows(device)
+	if err != nil {
+		statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+		vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+	}
+	/*
+	err = vpv.delDsDhcp6Flows(device)
+	if err != nil {
+		statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+		vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+	}*/
+	return nil
+}
+
+func (vpv *VoltPortVnet) delDsDhcp4Flows(device *VoltDevice) error {
+	flows, err := vpv.BuildDsDhcpFlows()
+	if err == nil {
+		return vpv.RemoveFlows(device, flows)
+	}
+	logger.Errorw(ctx, "DS DHCP Flow Delete Failed", log.Fields{"Reason": err.Error()})
+	return err
+}
+
+/*
+func (vpv *VoltPortVnet) delDsDhcp6Flows(device *VoltDevice) error {
+	flows, err := vpv.BuildDsDhcp6Flows()
+	if err == nil {
+		return vpv.RemoveFlows(device, flows)
+	}
+	logger.Errorw(ctx, "DS DHCP6 Flow Delete Failed", log.Fields{"Reason": err.Error()})
+	return err
+}*/
+
+// AddUsArpFlows pushes the ARP flows to the VOLTHA via the controller
+func (vpv *VoltPortVnet) AddUsArpFlows() error {
+
+	var vd *VoltDevice
+	device := vpv.Device
+	if vd = GetApplication().GetDevice(device); vd != nil {
+		if vd.State != controller.DeviceStateUP {
+			logger.Errorw(ctx, "Skipping US ARP Flow Push - Device state DOWN", log.Fields{"Port": vpv.Port, "SVLAN": vpv.SVlan, "CVLAN": vpv.CVlan, "UNIVlan": vpv.UniVlan, "device": device})
+			return nil
+		}
+	} else {
+		logger.Errorw(ctx, "US ARP Flow Push Failed- Device not found", log.Fields{"Port": vpv.Port, "SVLAN": vpv.SVlan, "CVLAN": vpv.CVlan, "UNIVlan": vpv.UniVlan, "device": device})
+		return errorCodes.ErrDeviceNotFound
+	}
+
+	flows, err := vpv.BuildUsArpFlows()
+	if err == nil {
+		logger.Debugw(ctx, "Adding US ARP flows", log.Fields{"Device": device})
+		if err1 := vpv.PushFlows(vd, flows); err1 != nil {
+			return err1
+		}
+	} else {
+		logger.Errorw(ctx, "US ARP Flow Add Failed", log.Fields{"Reason": err.Error(), "Device": device})
+		return err
+	}
+	return nil
+}
+
+// DelUsArpFlows delete the ARP flows applied for this Vnet instantiated on the port
+// Write the status of the VPV to the DB once the delete is scheduled
+// for dispatch
+func (vpv *VoltPortVnet) DelUsArpFlows() error {
+	device, err := GetApplication().GetDeviceFromPort(vpv.Port)
+	if err != nil {
+		return err
+	}
+	flows, err := vpv.BuildUsArpFlows()
+	if err == nil {
+		return vpv.RemoveFlows(device, flows)
+	}
+	logger.Errorw(ctx, "US ARP Flow Delete Failed", log.Fields{"Reason": err.Error()})
+	return err
+}
+
+// AddUsPppoeFlows pushes the PPPoE flows to the VOLTHA via the controller
+func (vpv *VoltPortVnet) AddUsPppoeFlows() error {
+	logger.Debugw(ctx, "Adding US PPPoE flows", log.Fields{"STAG": vpv.SVlan, "CTAG": vpv.CVlan, "Device": vpv.Device})
+
+	var vd *VoltDevice
+	device := vpv.Device
+
+	if vd = GetApplication().GetDevice(device); vd != nil {
+		if vd.State != controller.DeviceStateUP {
+			logger.Errorw(ctx, "Skipping US PPPoE Flow Push - Device state DOWN", log.Fields{"Port": vpv.Port, "SVLAN": vpv.SVlan, "CVLAN": vpv.CVlan, "UNIVlan": vpv.UniVlan, "device": device})
+			return nil
+		}
+	} else {
+		logger.Errorw(ctx, "US PPPoE Flow Push Failed- Device not found", log.Fields{"Port": vpv.Port, "SVLAN": vpv.SVlan, "CVLAN": vpv.CVlan, "UNIVlan": vpv.UniVlan, "device": device})
+		return errorCodes.ErrDeviceNotFound
+	}
+
+	if flows, err := vpv.BuildUsPppoeFlows(); err == nil {
+		logger.Debugw(ctx, "Adding US PPPoE flows", log.Fields{"Device": device})
+
+		if err1 := vpv.PushFlows(vd, flows); err1 != nil {
+			return err1
+		}
+	} else {
+		logger.Errorw(ctx, "US PPPoE Flow Add Failed", log.Fields{"Reason": err.Error(), "Device": device})
+		return err
+	}
+	return nil
+}
+
+// AddDsPppoeFlows to add downstream pppoe flows
+func (vpv *VoltPortVnet) AddDsPppoeFlows() error {
+	logger.Debugw(ctx, "Adding DS PPPoE flows", log.Fields{"STAG": vpv.SVlan, "CTAG": vpv.CVlan, "Device": vpv.Device})
+	var vd *VoltDevice
+	device := vpv.Device
+
+	if vd = GetApplication().GetDevice(device); vd != nil {
+		if vd.State != controller.DeviceStateUP {
+			logger.Errorw(ctx, "Skipping DS PPPoE Flow Push - Device state DOWN", log.Fields{"Port": vpv.Port, "SVLAN": vpv.SVlan, "CVLAN": vpv.CVlan, "UNIVlan": vpv.UniVlan, "device": device})
+			return nil
+		}
+	} else {
+		logger.Errorw(ctx, "DS PPPoE Flow Push Failed- Device not found", log.Fields{"Port": vpv.Port, "SVLAN": vpv.SVlan, "CVLAN": vpv.CVlan, "UNIVlan": vpv.UniVlan, "device": device})
+		return errorCodes.ErrDeviceNotFound
+	}
+
+	flows, err := vpv.BuildDsPppoeFlows()
+	if err == nil {
+
+		if err1 := vpv.PushFlows(vd, flows); err1 != nil {
+			return err1
+		}
+	} else {
+		logger.Errorw(ctx, "DS PPPoE Flow Add Failed", log.Fields{"Reason": err.Error()})
+		return err
+	}
+	return nil
+}
+
+// DelUsPppoeFlows delete the PPPoE flows applied for this Vnet instantiated on the port
+// Write the status of the VPV to the DB once the delete is scheduled
+// for dispatch
+func (vpv *VoltPortVnet) DelUsPppoeFlows() error {
+	logger.Debugw(ctx, "Deleting US PPPoE flows", log.Fields{"STAG": vpv.SVlan, "CTAG": vpv.CVlan, "Device": vpv.Device})
+	device, err := GetApplication().GetDeviceFromPort(vpv.Port)
+	if err != nil {
+		return err
+	}
+	flows, err := vpv.BuildUsPppoeFlows()
+	if err == nil {
+		return vpv.RemoveFlows(device, flows)
+	}
+	logger.Errorw(ctx, "US PPPoE Flow Delete Failed", log.Fields{"Reason": err.Error()})
+	return err
+}
+
+// DelDsPppoeFlows delete the PPPoE flows applied for this Vnet instantiated on the port
+// Write the status of the VPV to the DB once the delete is scheduled
+// for dispatch
+func (vpv *VoltPortVnet) DelDsPppoeFlows() error {
+	logger.Debugw(ctx, "Deleting DS PPPoE flows", log.Fields{"STAG": vpv.SVlan, "CTAG": vpv.CVlan, "Device": vpv.Device})
+	device, err := GetApplication().GetDeviceFromPort(vpv.Port)
+	if err != nil {
+		return err
+	}
+	flows, err := vpv.BuildDsPppoeFlows()
+	if err == nil {
+		return vpv.RemoveFlows(device, flows)
+	}
+	logger.Errorw(ctx, "DS PPPoE Flow Delete Failed", log.Fields{"Reason": err.Error()})
+	return err
+}
+
+// AddIgmpFlows function pushes the IGMP flows to the VOLTHA via the controller
+func (vpv *VoltPortVnet) AddIgmpFlows() error {
+
+	if !vpv.IgmpFlowsApplied || vgcRebooted {
+		if vpv.MvlanProfileName == "" {
+			logger.Info(ctx, "Mvlan Profile not configured. Ignoring Igmp trap flow")
+			return nil
+		}
+		device, err := GetApplication().GetDeviceFromPort(vpv.Port)
+		if err != nil {
+			logger.Errorw(ctx, "Error getting device from port", log.Fields{"Port": vpv.Port, "Reason": err.Error()})
+			return err
+		} else if device.State != controller.DeviceStateUP {
+			logger.Warnw(ctx, "Device state Down. Ignoring US IGMP Flow Push", log.Fields{"Port": vpv.Port, "SVLAN": vpv.SVlan, "CVLAN": vpv.CVlan, "UNIVlan": vpv.UniVlan})
+			return nil
+		}
+		flows, err := vpv.BuildIgmpFlows()
+		if err == nil {
+			for cookie := range flows.SubFlows {
+				if vd := GetApplication().GetDevice(device.Name); vd != nil {
+					cookie := strconv.FormatUint(cookie, 10)
+					fe := &FlowEvent{
+						eType:     EventTypeUsIgmpFlowAdded,
+						cookie:    cookie,
+						eventData: vpv,
+					}
+					vd.RegisterFlowAddEvent(cookie, fe)
+				}
+			}
+			if err1 := cntlr.GetController().AddFlows(vpv.Port, device.Name, flows); err1 != nil {
+				return err1
+			}
+		} else {
+			logger.Errorw(ctx, "IGMP Flow Add Failed", log.Fields{"Reason": err.Error()})
+			return err
+		}
+		vpv.IgmpFlowsApplied = true
+		vpv.WriteToDb()
+	}
+	return nil
+}
+
+// DelIgmpFlows delete the IGMP flows applied for this Vnet instantiated on the port
+// Write the status of the VPV to the DB once the delete is scheduled
+// for dispatch
+func (vpv *VoltPortVnet) DelIgmpFlows() error {
+
+	if vpv.IgmpFlowsApplied || vgcRebooted {
+		device, err := GetApplication().GetDeviceFromPort(vpv.Port)
+		if err != nil {
+			logger.Errorw(ctx, "Error getting device from port", log.Fields{"Port": vpv.Port, "Reason": err.Error()})
+			return err
+		}
+		flows, err := vpv.BuildIgmpFlows()
+		if err == nil {
+			if err1 := vpv.RemoveFlows(device, flows); err1 != nil {
+				return err1
+			}
+		} else {
+			logger.Errorw(ctx, "IGMP Flow Add Failed", log.Fields{"Reason": err.Error()})
+			return err
+		}
+		vpv.IgmpFlowsApplied = false
+		vpv.WriteToDb()
+	}
+	return nil
+}
+
+// BuildUsDhcpFlows builds the US DHCP relay flows for a subscriber
+// The flows included by this function cover US only as the DS is
+// created either automatically by the VOLTHA or at the device level
+// earlier
+func (vpv *VoltPortVnet) BuildUsDhcpFlows() (*of.VoltFlow, error) {
+	flow := &of.VoltFlow{}
+	flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
+
+	logger.Infow(ctx, "Building US DHCP flow", log.Fields{"Port": vpv.Port})
+	subFlow := of.NewVoltSubFlow()
+	subFlow.SetTableID(0)
+
+	if GetApplication().GetVendorID() == Radisys {
+		if err := vpv.setUsMatchVlan(subFlow); err != nil {
+			return nil, err
+		}
+	} else {
+		subFlow.SetMatchVlan(vpv.UniVlan)
+		subFlow.SetSetVlan(vpv.CVlan)
+	}
+	subFlow.SetUdpv4Match()
+	subFlow.SrcPort = 68
+	subFlow.DstPort = 67
+	uniport, err := GetApplication().GetPortID(vpv.Port)
+	if err != nil {
+		logger.Errorw(ctx, "Failed to fetch uni port from vpv", log.Fields{"error": err, "port": vpv.Port})
+		return nil, err
+	}
+	subFlow.SetInPort(uniport)
+	// PortName and PortID to be used for validation of port before flow pushing
+	flow.PortID = uniport
+	flow.PortName = vpv.Port
+	subFlow.SetReportToController()
+
+	// Set techprofile, meterid of first service
+	vpv.services.Range(func(key, value interface{}) bool {
+		svc := value.(*VoltService)
+		writemetadata := uint64(svc.TechProfileID) << 32
+		subFlow.SetWriteMetadata(writemetadata)
+		subFlow.SetMeterID(svc.UsMeterID)
+		return false
+	})
+
+	subFlow.SetPcp(vpv.DhcpPbit)
+	// metadata := uint64(uniport)
+	// subFlow.SetWriteMetadata(metadata)
+	allowTransparent := 0
+	if vpv.AllowTransparent {
+		allowTransparent = 1
+	}
+	metadata := uint64(allowTransparent)<<56 | uint64(vpv.ONTEtherTypeClassification)<<36 | uint64(vpv.VlanControl)<<32 | uint64(vpv.UniVlan)<<16 | uint64(vpv.CVlan)
+	subFlow.SetTableMetadata(metadata)
+
+	//| 12-bit cvlan | 4 bits empty | <32-bits uniport>| 16-bits dhcp mask or flow mask |
+	subFlow.Cookie = uint64(vpv.CVlan)<<52 | uint64(uniport)<<16 | of.DhcpArpFlowMask | of.UsFlowMask
+	subFlow.Priority = of.DhcpFlowPriority
+
+	flow.SubFlows[subFlow.Cookie] = subFlow
+	logger.Infow(ctx, "Built US DHCP flow ", log.Fields{"cookie": subFlow.Cookie, "flow": flow})
+	return flow, nil
+}
+
+// BuildDsDhcpFlows to build the downstream dhcp flows
+func (vpv *VoltPortVnet) BuildDsDhcpFlows() (*of.VoltFlow, error) {
+
+	logger.Infow(ctx, "Building DS DHCP flow", log.Fields{"Port": vpv.Port, "ML": vpv.MacLearning, "Mac": vpv.MacAddr})
+	flow := &of.VoltFlow{}
+	flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
+	subFlow := of.NewVoltSubFlow()
+	subFlow.SetTableID(0)
+	// If dhcp trap rule is global rule, No need to match on vlan
+	if GetApplication().GetVendorID() == Radisys {
+		vpv.setDsMatchVlan(subFlow)
+	}
+	subFlow.SetUdpv4Match()
+	subFlow.SrcPort = 67
+	subFlow.DstPort = 68
+	uniport, _ := GetApplication().GetPortID(vpv.Port)
+	nni, err := GetApplication().GetNniPort(vpv.Device)
+	if err != nil {
+		return nil, err
+	}
+	nniport, err := GetApplication().GetPortID(nni)
+	if err != nil {
+		return nil, err
+	}
+	subFlow.SetInPort(nniport)
+	// PortName and PortID to be used for validation of port before flow pushing
+	flow.PortID = uniport
+	flow.PortName = vpv.Port
+	// metadata := uint64(uniport)
+	// subFlow.SetWriteMetadata(metadata)
+	allowTransparent := 0
+	if vpv.AllowTransparent {
+		allowTransparent = 1
+	}
+	metadata := uint64(allowTransparent)<<56 | uint64(vpv.ONTEtherTypeClassification)<<36 | uint64(vpv.VlanControl)<<32 | uint64(vpv.UniVlan)<<16 | uint64(vpv.CVlan)
+	subFlow.SetTableMetadata(metadata)
+	subFlow.SetReportToController()
+	//| 12-bit cvlan | 4 bits empty | <32-bits uniport>| 16-bits dhcp mask or flow mask |
+	subFlow.Cookie = uint64(vpv.CVlan)<<52 | uint64(uniport)<<16 | of.DhcpArpFlowMask | of.DsFlowMask
+	subFlow.Priority = of.DhcpFlowPriority
+
+	flow.SubFlows[subFlow.Cookie] = subFlow
+	logger.Infow(ctx, "Built DS DHCP flow ", log.Fields{"cookie": subFlow.Cookie, "Flow": flow})
+
+	return flow, nil
+}
+
+// BuildUsDhcp6Flows to trap the DHCPv6 packets to be reported to the
+// application.
+func (vpv *VoltPortVnet) BuildUsDhcp6Flows() (*of.VoltFlow, error) {
+	flow := &of.VoltFlow{}
+	flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
+
+	logger.Infow(ctx, "Building US DHCPv6 flow", log.Fields{"Port": vpv.Port})
+	subFlow := of.NewVoltSubFlow()
+	subFlow.SetTableID(0)
+
+	subFlow.SetMatchVlan(vpv.UniVlan)
+	subFlow.SetSetVlan(vpv.CVlan)
+	subFlow.SetUdpv6Match()
+	subFlow.SrcPort = 546
+	subFlow.DstPort = 547
+	uniport, err := GetApplication().GetPortID(vpv.Port)
+	if err != nil {
+		return nil, err
+	}
+	// Set techprofile, meterid of first service
+	vpv.services.Range(func(key, value interface{}) bool {
+		svc := value.(*VoltService)
+		writemetadata := uint64(svc.TechProfileID) << 32
+		subFlow.SetWriteMetadata(writemetadata)
+		subFlow.SetMeterID(svc.UsMeterID)
+		return false
+	})
+	subFlow.SetInPort(uniport)
+	// PortName and PortID to be used for validation of port before flow pushing
+	flow.PortID = uniport
+	flow.PortName = vpv.Port
+	//subFlow.SetMeterId(vpv.UsDhcpMeterId)
+	// metadata := uint64(uniport)
+	// subFlow.SetWriteMetadata(metadata)
+	allowTransparent := 0
+	if vpv.AllowTransparent {
+		allowTransparent = 1
+	}
+	metadata := uint64(allowTransparent)<<56 | uint64(vpv.ONTEtherTypeClassification)<<36 | uint64(vpv.VlanControl)<<32 | uint64(vpv.UniVlan)<<16 | uint64(vpv.CVlan)
+	subFlow.SetTableMetadata(metadata)
+	subFlow.SetReportToController()
+	//| 12-bit cvlan | 4 bits empty | <32-bits uniport>| 16-bits dhcp mask or flow mask |
+	subFlow.Cookie = uint64(vpv.CVlan)<<52 | uint64(uniport)<<16 | of.Dhcpv6FlowMask | of.UsFlowMask
+	subFlow.Priority = of.DhcpFlowPriority
+
+	flow.SubFlows[subFlow.Cookie] = subFlow
+	logger.Infow(ctx, "Built US DHCPv6 flow", log.Fields{"cookie": subFlow.Cookie, "flow": flow})
+	return flow, nil
+}
+
+// BuildDsDhcp6Flows to trap the DHCPv6 packets to be reported to the
+// application.
+func (vpv *VoltPortVnet) BuildDsDhcp6Flows() (*of.VoltFlow, error) {
+	logger.Infow(ctx, "Building DS DHCPv6 flow", log.Fields{"Port": vpv.Port, "ML": vpv.MacLearning, "Mac": vpv.MacAddr})
+
+	flow := &of.VoltFlow{}
+	flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
+	subFlow := of.NewVoltSubFlow()
+	subFlow.SetTableID(0)
+
+	vpv.setDsMatchVlan(subFlow)
+	subFlow.SetUdpv6Match()
+	subFlow.SrcPort = 547
+	subFlow.DstPort = 547
+	uniport, _ := GetApplication().GetPortID(vpv.Port)
+	nni, err := GetApplication().GetNniPort(vpv.Device)
+	if err != nil {
+		return nil, err
+	}
+	nniport, err := GetApplication().GetPortID(nni)
+	if err != nil {
+		return nil, err
+	}
+	subFlow.SetInPort(nniport)
+	// PortName and PortID to be used for validation of port before flow pushing
+	flow.PortID = uniport
+	flow.PortName = vpv.Port
+	// metadata := uint64(uniport)
+	// subFlow.SetWriteMetadata(metadata)
+	allowTransparent := 0
+	if vpv.AllowTransparent {
+		allowTransparent = 1
+	}
+	metadata := uint64(allowTransparent)<<56 | uint64(vpv.ONTEtherTypeClassification)<<36 | uint64(vpv.VlanControl)<<32 | uint64(vpv.UniVlan)<<16 | uint64(vpv.CVlan)
+	subFlow.SetTableMetadata(metadata)
+	subFlow.SetReportToController()
+	//| 12-bit cvlan | 4 bits empty | <32-bits uniport>| 16-bits dhcp mask or flow mask |
+	subFlow.Cookie = uint64(vpv.CVlan)<<52 | uint64(uniport)<<16 | of.Dhcpv6FlowMask | of.DsFlowMask
+	subFlow.Priority = of.DhcpFlowPriority
+
+	flow.SubFlows[subFlow.Cookie] = subFlow
+	logger.Infow(ctx, "Built DS DHCPv6 flow", log.Fields{"cookie": subFlow.Cookie, "flow": flow})
+	return flow, nil
+}
+
+// BuildUsArpFlows builds the US ARP relay flows for a subscriber
+// The flows included by this function cover US only as the DS is
+// created either automatically by the VOLTHA or at the device level
+// earlier
+func (vpv *VoltPortVnet) BuildUsArpFlows() (*of.VoltFlow, error) {
+	flow := &of.VoltFlow{}
+	flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
+
+	logger.Infow(ctx, "Building US ARP flow", log.Fields{"Port": vpv.Port})
+	subFlow := of.NewVoltSubFlow()
+	subFlow.SetTableID(0)
+
+	if vpv.MacLearning == MacLearningNone && NonZeroMacAddress(vpv.MacAddr) {
+		subFlow.SetMatchSrcMac(vpv.MacAddr)
+	}
+
+	subFlow.SetMatchDstMac(BroadcastMAC)
+	if err := vpv.setUsMatchVlan(subFlow); err != nil {
+		return nil, err
+	}
+	subFlow.SetArpMatch()
+	uniport, err := GetApplication().GetPortID(vpv.Port)
+	if err != nil {
+		return nil, err
+	}
+	subFlow.SetInPort(uniport)
+	// PortName and PortID to be used for validation of port before flow pushing
+	flow.PortID = uniport
+	flow.PortName = vpv.Port
+	subFlow.SetReportToController()
+	allowTransparent := 0
+	if vpv.AllowTransparent {
+		allowTransparent = 1
+	}
+	metadata := uint64(uniport)
+	subFlow.SetWriteMetadata(metadata)
+	metadata = uint64(allowTransparent)<<56 | uint64(vpv.ONTEtherTypeClassification)<<36 | uint64(vpv.VlanControl)<<32 | uint64(vpv.UniVlan)<<16 | uint64(vpv.CVlan)
+	subFlow.SetTableMetadata(metadata)
+	subFlow.Cookie = uint64(vpv.CVlan)<<52 | uint64(uniport)<<32 | of.DhcpArpFlowMask | of.UsFlowMask
+	subFlow.Priority = of.ArpFlowPriority
+
+	flow.SubFlows[subFlow.Cookie] = subFlow
+	logger.Infow(ctx, "Built US ARP flow ", log.Fields{"cookie": subFlow.Cookie, "flow": flow})
+	return flow, nil
+}
+
+// setUsMatchVlan to set upstream match vlan
+func (vpv *VoltPortVnet) setUsMatchVlan(flow *of.VoltSubFlow) error {
+	switch vpv.VlanControl {
+	case None:
+		flow.SetMatchVlan(vpv.SVlan)
+	case ONUCVlanOLTSVlan:
+		flow.SetMatchVlan(vpv.CVlan)
+	case OLTCVlanOLTSVlan:
+		flow.SetMatchVlan(vpv.UniVlan)
+		//flow.SetSetVlan(vpv.CVlan)
+	case ONUCVlan:
+		flow.SetMatchVlan(vpv.SVlan)
+	case OLTSVlan:
+		flow.SetMatchVlan(vpv.UniVlan)
+		//flow.SetSetVlan(vpv.SVlan)
+	default:
+		logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vpv.VlanControl})
+		return errorCodes.ErrInvalidParamInRequest
+	}
+	return nil
+}
+
+// BuildUsPppoeFlows to build upstream pppoe flows
+func (vpv *VoltPortVnet) BuildUsPppoeFlows() (*of.VoltFlow, error) {
+
+	flow := &of.VoltFlow{}
+	flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
+	logger.Infow(ctx, "Building US PPPoE flow", log.Fields{"Port": vpv.Port})
+	subFlow := of.NewVoltSubFlow()
+	subFlow.SetTableID(0)
+
+	if vpv.MacLearning == MacLearningNone && NonZeroMacAddress(vpv.MacAddr) {
+		subFlow.SetMatchSrcMac(vpv.MacAddr)
+	}
+
+	if err := vpv.setUsMatchVlan(subFlow); err != nil {
+		return nil, err
+	}
+	subFlow.SetPppoeDiscoveryMatch()
+	uniport, err := GetApplication().GetPortID(vpv.Port)
+	if err != nil {
+		return nil, err
+	}
+	subFlow.SetInPort(uniport)
+	subFlow.SetReportToController()
+	// PortName and PortID to be used for validation of port before flow pushing
+	flow.PortID = uniport
+	flow.PortName = vpv.Port
+
+	allowTransparent := 0
+	if vpv.AllowTransparent {
+		allowTransparent = 1
+	}
+	metadata := uint64(uniport)
+	subFlow.SetWriteMetadata(metadata)
+
+	metadata = uint64(allowTransparent)<<56 | uint64(vpv.ONTEtherTypeClassification)<<36 | uint64(vpv.VlanControl)<<32 | uint64(vpv.UniVlan)<<16 | uint64(vpv.CVlan)
+	subFlow.SetTableMetadata(metadata)
+
+	//| 12-bit cvlan | 4 bits empty | <32-bits uniport>| 16-bits pppoe mask or flow mask |
+	subFlow.Cookie = uint64(vpv.CVlan)<<52 | uint64(uniport)<<16 | of.PppoeFlowMask | of.UsFlowMask
+	subFlow.Priority = of.PppoeFlowPriority
+
+	flow.SubFlows[subFlow.Cookie] = subFlow
+	logger.Infow(ctx, "Built US PPPoE flow ", log.Fields{"cookie": subFlow.Cookie, "flow": flow})
+	return flow, nil
+}
+
+// BuildDsPppoeFlows to build downstream pppoe flows
+func (vpv *VoltPortVnet) BuildDsPppoeFlows() (*of.VoltFlow, error) {
+
+	logger.Infow(ctx, "Building DS PPPoE flow", log.Fields{"Port": vpv.Port, "ML": vpv.MacLearning, "Mac": vpv.MacAddr})
+	flow := &of.VoltFlow{}
+	flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
+	subFlow := of.NewVoltSubFlow()
+	subFlow.SetTableID(0)
+
+	vpv.setDsMatchVlan(subFlow)
+	subFlow.SetPppoeDiscoveryMatch()
+
+	if NonZeroMacAddress(vpv.MacAddr) {
+		subFlow.SetMatchDstMac(vpv.MacAddr)
+	}
+
+	uniport, _ := GetApplication().GetPortID(vpv.Port)
+	nni, err := GetApplication().GetNniPort(vpv.Device)
+	if err != nil {
+		return nil, err
+	}
+	nniport, err := GetApplication().GetPortID(nni)
+	if err != nil {
+		return nil, err
+	}
+	subFlow.SetInPort(nniport)
+	// PortName and PortID to be used for validation of port before flow pushing
+	flow.PortID = uniport
+	flow.PortName = vpv.Port
+	metadata := uint64(uniport)
+	subFlow.SetWriteMetadata(metadata)
+	allowTransparent := 0
+	if vpv.AllowTransparent {
+		allowTransparent = 1
+	}
+	metadata = uint64(allowTransparent)<<56 | uint64(vpv.ONTEtherTypeClassification)<<36 | uint64(vpv.VlanControl)<<32 | uint64(vpv.UniVlan)<<16 | uint64(vpv.CVlan)
+	subFlow.SetTableMetadata(metadata)
+	subFlow.SetReportToController()
+	//| 12-bit cvlan | 4 bits empty | <32-bits uniport>| 16-bits dhcp mask or flow mask |
+	subFlow.Cookie = uint64(vpv.CVlan)<<52 | uint64(uniport)<<16 | of.PppoeFlowMask | of.DsFlowMask
+	subFlow.Priority = of.PppoeFlowPriority
+
+	flow.SubFlows[subFlow.Cookie] = subFlow
+	logger.Infow(ctx, "Built DS DHCP flow ", log.Fields{"cookie": subFlow.Cookie, "Flow": flow})
+	return flow, nil
+}
+
+// setDsMatchVlan to set downstream match vlan
+func (vpv *VoltPortVnet) setDsMatchVlan(flow *of.VoltSubFlow) {
+	switch vpv.VlanControl {
+	case None:
+		flow.SetMatchVlan(vpv.SVlan)
+	case ONUCVlanOLTSVlan,
+		OLTCVlanOLTSVlan,
+		ONUCVlan,
+		OLTSVlan:
+		flow.SetMatchVlan(vpv.SVlan)
+	default:
+		logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vpv.VlanControl})
+	}
+}
+
+// BuildIgmpFlows builds the US IGMP flows for a subscriber. IGMP requires flows only
+// in the US direction.
+func (vpv *VoltPortVnet) BuildIgmpFlows() (*of.VoltFlow, error) {
+	logger.Infow(ctx, "Building US IGMP Flow", log.Fields{"Port": vpv.Port})
+	mvp := GetApplication().GetMvlanProfileByName(vpv.MvlanProfileName)
+	if mvp == nil {
+		return nil, errors.New("Mvlan Profile configured not found")
+	}
+	mvlan := mvp.GetUsMatchVlan()
+	flow := &of.VoltFlow{}
+	flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
+	subFlow := of.NewVoltSubFlow()
+	subFlow.SetTableID(0)
+
+        if GetApplication().GetVendorID() == Radisys {
+                if err := vpv.setUsMatchVlan(subFlow); err != nil {
+                        return nil, err
+                }
+        } else {
+                subFlow.SetMatchVlan(vpv.UniVlan)
+                subFlow.SetSetVlan(vpv.CVlan)
+        }
+
+	uniport, err := GetApplication().GetPortID(vpv.Port)
+	if err != nil {
+		return nil, err
+	}
+	subFlow.SetInPort(uniport)
+	// PortName and PortID to be used for validation of port before flow pushing
+	flow.PortID = uniport
+	flow.PortName = vpv.Port
+
+	if vpv.MacLearning == MacLearningNone && NonZeroMacAddress(vpv.MacAddr) {
+		subFlow.SetMatchSrcMac(vpv.MacAddr)
+	}
+	logger.Infow(ctx, "Mvlan", log.Fields{"mvlan": mvlan})
+	//metadata := uint64(mvlan)
+
+	if vpv.McastService {
+		metadata := uint64(vpv.McastUsMeterID)
+		metadata = metadata | uint64(vpv.McastTechProfileID)<<32
+		subFlow.SetMatchPbit(vpv.McastPbit)
+		subFlow.SetMeterID(vpv.McastUsMeterID)
+		subFlow.SetWriteMetadata(metadata)
+	} else {
+		// Set techprofile, meterid of first service
+		vpv.services.Range(func(key, value interface{}) bool {
+			svc := value.(*VoltService)
+			writemetadata := uint64(svc.TechProfileID) << 32
+			subFlow.SetWriteMetadata(writemetadata)
+			subFlow.SetMeterID(svc.UsMeterID)
+			return false
+		})
+	}
+
+	allowTransparent := 0
+	if vpv.AllowTransparent {
+		allowTransparent = 1
+	}
+	metadata := uint64(allowTransparent)<<56 | uint64(vpv.SchedID)<<40 | uint64(vpv.ONTEtherTypeClassification)<<36 | uint64(vpv.VlanControl)<<32 | uint64(vpv.UniVlan)<<16 | uint64(vpv.CVlan)
+	subFlow.SetTableMetadata(metadata)
+	subFlow.SetIgmpMatch()
+	subFlow.SetReportToController()
+	//| 16 bits empty | <32-bits uniport>| 16-bits igmp mask or flow mask |
+	subFlow.Cookie = uint64(uniport)<<16 | of.IgmpFlowMask | of.UsFlowMask
+	subFlow.Priority = of.IgmpFlowPriority
+
+	flow.SubFlows[subFlow.Cookie] = subFlow
+	logger.Infow(ctx, "Built US IGMP flow ", log.Fields{"cookie": subFlow.Cookie, "flow": flow})
+	return flow, nil
+}
+
+// WriteToDb for writing to database
+func (vpv *VoltPortVnet) WriteToDb() {
+	if vpv.DeleteInProgress {
+		logger.Warnw(ctx, "Skipping Redis Update for VPV, VPV delete in progress", log.Fields{"Vnet": vpv.VnetName, "Port": vpv.Port})
+		return
+	}
+	vpv.ForceWriteToDb()
+}
+
+//ForceWriteToDb force commit a VPV to the DB
+func (vpv *VoltPortVnet) ForceWriteToDb() {
+	vpv.PendingFlowLock.RLock()
+	defer vpv.PendingFlowLock.RUnlock()
+	vpv.Version = database.PresentVersionMap[database.VpvPath]
+	if b, err := json.Marshal(vpv); err == nil {
+		if err := db.PutVpv(vpv.Port, uint16(vpv.SVlan), uint16(vpv.CVlan), uint16(vpv.UniVlan), string(b)); err != nil {
+			logger.Warnw(ctx, "VPV write to DB failed", log.Fields{"port": vpv.Port, "SVlan": vpv.SVlan, "CVlan": vpv.CVlan,
+				"UniVlan": vpv.UniVlan, "Error": err})
+		}
+	}
+}
+
+// DelFromDb for deleting from database
+func (vpv *VoltPortVnet) DelFromDb() {
+	logger.Debugw(ctx, "Deleting VPV from DB", log.Fields{"Port": vpv.Port, "SVLAN": vpv.SVlan, "CVLAN": vpv.CVlan})
+	_ = db.DelVpv(vpv.Port, uint16(vpv.SVlan), uint16(vpv.CVlan), uint16(vpv.UniVlan))
+}
+
+// ClearAllServiceFlags to clear all service flags
+func (vpv *VoltPortVnet) ClearAllServiceFlags() {
+	vpv.services.Range(ClearFlagsInService)
+}
+
+// ClearAllVpvFlags to clear all vpv flags
+func (vpv *VoltPortVnet) ClearAllVpvFlags() {
+	vpv.PendingFlowLock.Lock()
+	vpv.FlowsApplied = false
+	vpv.IgmpFlowsApplied = false
+	vpv.PendingDeleteFlow = make(map[string]bool)
+	vpv.PendingFlowLock.Unlock()
+	vpv.WriteToDb()
+	logger.Debugw(ctx, "Cleared Flow Flags for VPV",
+		log.Fields{"device": vpv.Device, "port": vpv.Port,
+			"svlan": vpv.SVlan, "cvlan": vpv.CVlan, "univlan": vpv.UniVlan})
+}
+
+// CreateVpvFromString to create vpv from string
+func (va *VoltApplication) CreateVpvFromString(b []byte, hash string) {
+	var vpv VoltPortVnet
+	if err := json.Unmarshal(b, &vpv); err == nil {
+		vnetsByPortsSliceIntf, ok := va.VnetsByPort.Load(vpv.Port)
+		if !ok {
+			va.VnetsByPort.Store(vpv.Port, []*VoltPortVnet{})
+			vnetsByPortsSliceIntf = []*VoltPortVnet{}
+		}
+		vpv.servicesCount = atomic.NewUint64(0)
+		vnetsByPortsSlice := vnetsByPortsSliceIntf.([]*VoltPortVnet)
+		vnetsByPortsSlice = append(vnetsByPortsSlice, &vpv)
+		va.VnetsByPort.Store(vpv.Port, vnetsByPortsSlice)
+		va.UpdateMacInPortMap(vpv.MacAddr, vpv.Port)
+		if vnet := va.GetVnetByName(vpv.VnetName); vnet != nil {
+			vnet.associatePortToVnet(vpv.Port)
+		}
+
+		if vpv.DeleteInProgress {
+			va.VoltPortVnetsToDelete[&vpv] = true
+			logger.Warnw(ctx, "VPV (restored) to be deleted", log.Fields{"Port": vpv.Port, "Vnet": vpv.VnetName})
+		}
+		logger.Debugw(ctx, "Added VPV from string", log.Fields{"port": vpv.Port, "svlan": vpv.SVlan, "cvlan": vpv.CVlan, "univlan": vpv.UniVlan})
+	}
+}
+
+// RestoreVpvsFromDb to restore vpvs from database
+func (va *VoltApplication) RestoreVpvsFromDb() {
+	// VNETS must be learnt first
+	vpvs, _ := db.GetVpvs()
+	for hash, vpv := range vpvs {
+		b, ok := vpv.Value.([]byte)
+		if !ok {
+			logger.Warn(ctx, "The value type is not []byte")
+			continue
+		}
+		va.CreateVpvFromString(b, hash)
+	}
+}
+
+// GetVnetByPort : VNET related functionality of VOLT Application here on.
+// Get the VNET from a port. The port identity is passed as device and port identities in string.
+// The identity of the VNET is the SVLAN and the CVLAN. Only if the both match the VLAN
+// is assumed to have matched. TODO: 1:1 should be treated differently and needs to be addressed
+func (va *VoltApplication) GetVnetByPort(port string, svlan of.VlanType, cvlan of.VlanType, univlan of.VlanType) *VoltPortVnet {
+	if _, ok := va.VnetsByPort.Load(port); !ok {
+		return nil
+	}
+	vpvs, _ := va.VnetsByPort.Load(port)
+	for _, vpv := range vpvs.([]*VoltPortVnet) {
+		if vpv.MatchesVlans(svlan, cvlan, univlan) {
+			return vpv
+		}
+	}
+	return nil
+}
+
+// AddVnetToPort to add vnet to port
+func (va *VoltApplication) AddVnetToPort(port string, vvnet *VoltVnet, vs *VoltService) *VoltPortVnet {
+	// The VNET is not on the port and is to be added
+	logger.Debugw(ctx, "Adding VNET to Port", log.Fields{"Port": port, "VNET": vvnet.Name})
+	vpv := NewVoltPortVnet(vvnet)
+	vpv.MacLearning = vvnet.MacLearning
+	vpv.Port = port
+	vvnet.associatePortToVnet(port)
+	if _, ok := va.VnetsByPort.Load(port); !ok {
+		va.VnetsByPort.Store(port, []*VoltPortVnet{})
+	}
+	vpvsIntf, _ := va.VnetsByPort.Load(port)
+	vpvs := vpvsIntf.([]*VoltPortVnet)
+	vpvs = append(vpvs, vpv)
+	va.VnetsByPort.Store(port, vpvs)
+	va.UpdateMacInPortMap(vpv.MacAddr, vpv.Port)
+
+	vpv.VpvLock.Lock()
+	defer vpv.VpvLock.Unlock()
+
+	// Add the service that is causing the VNET to be added to the port
+	vpv.AddSvc(vs)
+
+	// Process the PORT UP if the port is already up
+	d, err := va.GetDeviceFromPort(port)
+	if err == nil {
+		vpv.setDevice(d.Name)
+		p := d.GetPort(port)
+		if p != nil {
+
+			if vs.PonPort != 0xFF && vs.PonPort != p.PonPort {
+				logger.Errorw(ctx, "UNI port discovered on wrong PON Port. Dropping Flow Push for VPV", log.Fields{"Device": d.Name, "Port": port, "DetectedPon": p.PonPort, "ExpectedPon": vs.PonPort, "Vnet": vpv.VnetName})
+			} else {
+				logger.Infow(ctx, "Checking UNI port state", log.Fields{"State": p.State})
+				if d.State == controller.DeviceStateUP && p.State == PortStateUp {
+					vpv.PortUpInd(d, port)
+				}
+			}
+		}
+	}
+	vpv.WriteToDb()
+	return vpv
+}
+
+// DelVnetFromPort for deleting vnet from port
+func (va *VoltApplication) DelVnetFromPort(port string, vpv *VoltPortVnet) {
+
+	//Delete DHCP Session
+	delDhcpSessions(vpv.LearntMacAddr, vpv.SVlan, vpv.CVlan, vpv.DHCPv6DUID)
+
+	//Delete PPPoE session
+	delPppoeIaSessions(vpv.LearntMacAddr, vpv.SVlan, vpv.CVlan)
+
+	//Delete Mac from MacPortMap
+	va.DeleteMacInPortMap(vpv.MacAddr)
+
+	//Delete VPV
+	vpvsIntf, ok := va.VnetsByPort.Load(port)
+	if !ok {
+		return
+	}
+	vpvs := vpvsIntf.([]*VoltPortVnet)
+	for i, lvpv := range vpvs {
+		if lvpv == vpv {
+			logger.Debugw(ctx, "Deleting VPV from port", log.Fields{"Port": vpv.Port, "SVLAN": vpv.SVlan, "CVLAN": vpv.CVlan,
+				"UNIVLAN": vpv.UniVlan})
+
+			vpvs = append(vpvs[0:i], vpvs[i+1:]...)
+
+			vpv.DeleteInProgress = true
+			vpv.ForceWriteToDb()
+
+			va.VnetsByPort.Store(port, vpvs)
+			vpv.DelTrapFlows()
+			vpv.DelHsiaFlows()
+			va.DisassociateVpvsFromDevice(vpv.Device, vpv)
+			vpv.PendingFlowLock.RLock()
+			if len(vpv.PendingDeleteFlow) == 0 {
+				vpv.DelFromDb()
+			}
+			if vnet := va.GetVnetByName(vpv.VnetName); vnet != nil {
+				vnet.disassociatePortFromVnet(vpv.Device, vpv.Port)
+			}
+			vpv.PendingFlowLock.RUnlock()
+			return
+		}
+	}
+}
+
+// RestoreVnetsFromDb to restore vnet from port
+func (va *VoltApplication) RestoreVnetsFromDb() {
+	// VNETS must be learnt first
+	vnets, _ := db.GetVnets()
+	for _, net := range vnets {
+		b, ok := net.Value.([]byte)
+		if !ok {
+			logger.Warn(ctx, "The value type is not []byte")
+			continue
+		}
+		var vnet VoltVnet
+		err := json.Unmarshal(b, &vnet)
+		if err != nil {
+			logger.Warn(ctx, "Unmarshal of VNET failed")
+			continue
+		}
+		logger.Debugw(ctx, "Retrieved VNET", log.Fields{"VNET": vnet.VnetConfig})
+		if err := va.AddVnet(vnet.VnetConfig, &vnet.VnetOper); err != nil {
+			logger.Warnw(ctx, "Add Vnet Failed", log.Fields{"Config": vnet.VnetConfig, "Error": err})
+		}
+
+		if vnet.DeleteInProgress {
+			va.VnetsToDelete[vnet.Name] = true
+			logger.Warnw(ctx, "Vnet (restored) to be deleted", log.Fields{"Vnet": vnet.Name})
+		}
+
+	}
+}
+
+// GetServiceFromCvlan : Locate a service based on the packet received. The packet contains VLANs that
+// are used as the key to locate the service. If more than one service is on the
+// same port (essentially a UNI of ONU), the services must be separated by different
+// CVLANs
+func (va *VoltApplication) GetServiceFromCvlan(device, port string, vlans []of.VlanType, priority uint8) *VoltService {
+	// Fetch the device first to make sure the device exists
+	dIntf, ok := va.DevicesDisc.Load(device)
+	if !ok {
+		return nil
+	}
+	d := dIntf.(*VoltDevice)
+
+	// If the port is NNI port, the services dont exist on it. The svc then
+	// must be obtained from a different context and is not included here
+	if port == d.NniPort {
+		return nil
+	}
+
+	//To return the matched service
+	var service *VoltService
+
+	// This is an access port and the port should have all the associated
+	// services which can be uniquely identified by the VLANs in the packet
+	vnets, ok := va.VnetsByPort.Load(port)
+
+	if !ok {
+		logger.Debugw(ctx, "No Vnets for port", log.Fields{"Port": port})
+		return nil
+	}
+	logger.Debugw(ctx, "Matching for VLANs", log.Fields{"VLANs": vlans, "Priority": priority})
+	for _, vnet := range vnets.([]*VoltPortVnet) {
+		logger.Infow(ctx, "Vnet", log.Fields{"Vnet": vnet})
+		switch vnet.VlanControl {
+		case ONUCVlanOLTSVlan:
+			service = vnet.MatchesPriority(priority)
+			if vnet.MatchesCvlan(vlans) && service != nil {
+				return service
+			}
+		case ONUCVlan,
+			None:
+			service = vnet.MatchesPriority(priority)
+			// In case of DHCP Flow - cvlan == VlanNone
+			// In case of HSIA Flow - cvlan == Svlan
+			if len(vlans) == 1 && (vlans[0] == vnet.SVlan || vlans[0] == of.VlanNone) && service != nil {
+				return service
+			}
+		case OLTCVlanOLTSVlan,
+			OLTSVlan:
+			service = vnet.MatchesPriority(priority)
+			if len(vlans) == 1 && vlans[0] == vnet.UniVlan && service != nil {
+				return service
+			}
+		default:
+			logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vnet.VlanControl})
+		}
+	}
+	return nil
+}
+
+// GetVnetFromFields : Locate a service based on the packet received. The packet contains VLANs that
+// are used as the key to locate the service. If more than one service is on the
+// same port (essentially a UNI of ONU), the services must be separated by different
+// CVLANs
+func (va *VoltApplication) GetVnetFromFields(device string, port string, vlans []of.VlanType, priority uint8) (*VoltPortVnet, *VoltService) {
+	// Fetch the device first to make sure the device exists
+	dIntf, ok := va.DevicesDisc.Load(device)
+	if !ok {
+		return nil, nil
+	}
+	d := dIntf.(*VoltDevice)
+
+	// If the port is NNI port, the services dont exist on it. The svc then
+	// must be obtained from a different context and is not included here
+	if port == d.NniPort {
+		return nil, nil
+	}
+
+	//To return the matched service
+	var service *VoltService
+
+	// This is an access port and the port should have all the associated
+	// services which can be uniquely identified by the VLANs in the packet
+	if vnets, ok := va.VnetsByPort.Load(port); ok {
+		logger.Debugw(ctx, "Matching for VLANs", log.Fields{"VLANs": vlans, "Priority": priority})
+		for _, vnet := range vnets.([]*VoltPortVnet) {
+			logger.Infow(ctx, "Vnet", log.Fields{"Vnet": vnet})
+			switch vnet.VlanControl {
+			case ONUCVlanOLTSVlan:
+				service = vnet.MatchesPriority(priority)
+				if vnet.MatchesCvlan(vlans) && service != nil {
+					return vnet, service
+				}
+			case ONUCVlan,
+				None:
+				service = vnet.MatchesPriority(priority)
+				if (len(vlans) == 1 || vnet.AllowTransparent) && vlans[0] == vnet.SVlan && service != nil {
+					return vnet, service
+				}
+			case OLTCVlanOLTSVlan,
+				OLTSVlan:
+				service = vnet.MatchesPriority(priority)
+				if (len(vlans) == 1 || vnet.AllowTransparent) && vlans[0] == vnet.UniVlan && service != nil {
+					return vnet, service
+				}
+			default:
+				logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vnet.VlanControl})
+			}
+		}
+	}
+	return nil, nil
+}
+
+// GetVnetFromPkt : Locate a service based on the packet received. The packet contains VLANs that
+// are used as the key to locate the service. If more than one service is on the
+// same port (essentially a UNI of ONU), the services must be separated by different
+// CVLANs
+func (va *VoltApplication) GetVnetFromPkt(device string, port string, pkt gopacket.Packet) (*VoltPortVnet, *VoltService) {
+	vlans := GetVlans(pkt)
+	priority := GetPriority(pkt)
+	return va.GetVnetFromFields(device, port, vlans, priority)
+}
+
+// PushDevFlowForVlan to push icmpv6 flows for vlan
+func (va *VoltApplication) PushDevFlowForVlan(vnet *VoltVnet) {
+	logger.Infow(ctx, "PushDevFlowForVlan", log.Fields{"SVlan": vnet.SVlan, "CVlan": vnet.CVlan})
+	pushflow := func(key interface{}, value interface{}) bool {
+		device := value.(*VoltDevice)
+		if !isDeviceInList(device.SerialNum, vnet.DevicesList) {
+			logger.Info(ctx, "Device not present in vnet device list", log.Fields{"Device": device.SerialNum})
+			return true
+		}
+		if device.State != controller.DeviceStateUP {
+			logger.Errorw(ctx, "Push Dev Flows Failed - Device state DOWN", log.Fields{"Port": device.NniPort, "Vlan": vnet.SVlan, "device": device})
+			return true
+		}
+		if applied, ok := device.VlanPortStatus.Load(uint16(vnet.SVlan)); !ok || !applied.(bool) {
+			logger.Errorw(ctx, "Push Dev Flows Failed - Vlan not enabled yet", log.Fields{"Port": device.NniPort, "Vlan": vnet.SVlan})
+			return true
+		}
+
+		if vnetListIntf, ok := device.ConfiguredVlanForDeviceFlows.Get(VnetKey(vnet.SVlan, vnet.CVlan, 0)); ok {
+			vnetList := vnetListIntf.(*util.ConcurrentMap)
+			vnetList.Set(vnet.Name, true)
+			device.ConfiguredVlanForDeviceFlows.Set(VnetKey(vnet.SVlan, vnet.CVlan, 0), vnetList)
+			logger.Infow(ctx, "Flow already pushed for these Vlans. Adding profile to list", log.Fields{"SVlan": vnet.SVlan, "CVlan": vnet.CVlan, "vnetList-len": vnetList.Length()})
+			return true
+		}
+		logger.Debugw(ctx, "Configuring Dev Flows Group for device ", log.Fields{"Device": device})
+		err := ProcessIcmpv6McGroup(device.Name, false)
+		if err != nil {
+			logger.Warnw(ctx, "Configuring Dev Flows Group for device failed ", log.Fields{"Device": device.Name, "err": err})
+			return true
+		}
+		if portID, err := va.GetPortID(device.NniPort); err == nil {
+			if state, _ := cntlr.GetController().GetPortState(device.Name, device.NniPort); state != cntlr.PortStateUp {
+				logger.Warnw(ctx, "Skipping Dev Flow Configuration - Port Down", log.Fields{"Device": device})
+				return true
+			}
+
+			//Pushing ICMPv6 Flow
+			flow := BuildICMPv6Flow(portID, vnet)
+			err = cntlr.GetController().AddFlows(device.NniPort, device.Name, flow)
+			if err != nil {
+				logger.Warnw(ctx, "Configuring ICMPv6 Flow for device failed ", log.Fields{"Device": device.Name, "err": err})
+				return true
+			}
+			logger.Infow(ctx, "ICMPv6 Flow Added to Queue", log.Fields{"flow": flow})
+
+			// Pushing ARP Flow
+			flow = BuildDSArpFlow(portID, vnet)
+			err = cntlr.GetController().AddFlows(device.NniPort, device.Name, flow)
+			if err != nil {
+				logger.Warnw(ctx, "Configuring ARP Flow for device failed ", log.Fields{"Device": device.Name, "err": err})
+				return true
+			}
+			logger.Infow(ctx, "ARP Flow Added to Queue", log.Fields{"flow": flow})
+
+			vnetList := util.NewConcurrentMap()
+			vnetList.Set(vnet.Name, true)
+			device.ConfiguredVlanForDeviceFlows.Set(VnetKey(vnet.SVlan, vnet.CVlan, 0), vnetList)
+		}
+		return true
+	}
+	va.DevicesDisc.Range(pushflow)
+}
+
+// PushDevFlowForDevice to push icmpv6 flows for device
+func (va *VoltApplication) PushDevFlowForDevice(device *VoltDevice) {
+	logger.Infow(ctx, "PushDevFlowForDevice", log.Fields{"device": device})
+
+	logger.Debugw(ctx, "Configuring ICMPv6 Group for device ", log.Fields{"Device": device.Name})
+	err := ProcessIcmpv6McGroup(device.Name, false)
+	if err != nil {
+		logger.Warnw(ctx, "Configuring ICMPv6 Group for device failed ", log.Fields{"Device": device.Name, "err": err})
+		return
+	}
+	pushicmpv6 := func(key, value interface{}) bool {
+		vnet := value.(*VoltVnet)
+		if vnetListIntf, ok := device.ConfiguredVlanForDeviceFlows.Get(VnetKey(vnet.SVlan, vnet.CVlan, 0)); ok {
+			vnetList := vnetListIntf.(*util.ConcurrentMap)
+			vnetList.Set(vnet.Name, true)
+			device.ConfiguredVlanForDeviceFlows.Set(VnetKey(vnet.SVlan, vnet.CVlan, 0), vnetList)
+			logger.Infow(ctx, "Flow already pushed for these Vlans. Adding profile to list", log.Fields{"SVlan": vnet.SVlan, "CVlan": vnet.CVlan, "vnetList-len": vnetList.Length()})
+			return true
+		}
+		nniPortID, err := va.GetPortID(device.NniPort)
+		if err != nil {
+			logger.Errorw(ctx, "Push ICMPv6 Failed - Failed to get NNI Port Id", log.Fields{"Port": device.NniPort, "Reason": err.Error})
+		}
+		if applied, ok := device.VlanPortStatus.Load(uint16(vnet.SVlan)); !ok || !applied.(bool) {
+			logger.Warnw(ctx, "Push ICMPv6 Failed - Vlan not enabled yet", log.Fields{"Port": device.NniPort, "Vlan": vnet.SVlan})
+			return true
+		}
+		flow := BuildICMPv6Flow(nniPortID, vnet)
+		err = cntlr.GetController().AddFlows(device.NniPort, device.Name, flow)
+		if err != nil {
+			logger.Warnw(ctx, "Configuring ICMPv6 Flow for device failed ", log.Fields{"Device": device.Name, "err": err})
+			return true
+		}
+		logger.Infow(ctx, "ICMP Flow Added to Queue", log.Fields{"flow": flow})
+
+		flow = BuildDSArpFlow(nniPortID, vnet)
+		err = cntlr.GetController().AddFlows(device.NniPort, device.Name, flow)
+		if err != nil {
+			logger.Warnw(ctx, "Configuring ARP Flow for device failed ", log.Fields{"Device": device.Name, "err": err})
+			return true
+		}
+		logger.Infow(ctx, "ARP Flow Added to Queue", log.Fields{"flow": flow})
+
+		vnetList := util.NewConcurrentMap()
+		vnetList.Set(vnet.Name, true)
+		device.ConfiguredVlanForDeviceFlows.Set(VnetKey(vnet.SVlan, vnet.CVlan, 0), vnetList)
+		return true
+	}
+	va.VnetsByName.Range(pushicmpv6)
+}
+
+// DeleteDevFlowForVlan to delete icmpv6 flow for vlan
+func (va *VoltApplication) DeleteDevFlowForVlan(vnet *VoltVnet) {
+	logger.Infow(ctx, "DeleteDevFlowForVlan", log.Fields{"SVlan": vnet.SVlan, "CVlan": vnet.CVlan})
+	delflows := func(key interface{}, value interface{}) bool {
+		device := value.(*VoltDevice)
+
+		if vnetListIntf, ok := device.ConfiguredVlanForDeviceFlows.Get(VnetKey(vnet.SVlan, vnet.CVlan, 0)); ok {
+			vnetList := vnetListIntf.(*util.ConcurrentMap)
+			vnetList.Remove(vnet.Name)
+			device.ConfiguredVlanForDeviceFlows.Set(VnetKey(vnet.SVlan, vnet.CVlan, 0), vnetList)
+			if vnetList.Length() != 0 {
+				logger.Warnw(ctx, "Similar VNet associated to diff service. Not removing ICMPv6 flow", log.Fields{"SVlan": vnet.SVlan, "CVlan": vnet.CVlan, "vnetList-len": vnetList.Length()})
+				return true
+			}
+		}
+		if portID, err := va.GetPortID(device.NniPort); err == nil {
+			if state, _ := cntlr.GetController().GetPortState(device.Name, device.NniPort); state != cntlr.PortStateUp {
+				logger.Warnw(ctx, "Skipping ICMPv6 Flow Deletion - Port Down", log.Fields{"Device": device})
+				return true
+			}
+			//Pushing ICMPv6 Flow
+			flow := BuildICMPv6Flow(portID, vnet)
+			flow.ForceAction = true
+			err := vnet.RemoveFlows(device, flow)
+			if err != nil {
+				logger.Warnw(ctx, "De-Configuring ICMPv6 Flow for device failed ", log.Fields{"Device": device.Name, "err": err})
+				return true
+			}
+			logger.Infow(ctx, "ICMPv6 Flow Delete Added to Queue", log.Fields{"flow": flow})
+
+			//Pushing ARP Flow
+			flow = BuildDSArpFlow(portID, vnet)
+			flow.ForceAction = true
+			err = vnet.RemoveFlows(device, flow)
+			if err != nil {
+				logger.Warnw(ctx, "De-Configuring ARP Flow for device failed ", log.Fields{"Device": device.Name, "err": err})
+				return true
+			}
+			logger.Infow(ctx, "ARP Flow Delete Added to Queue", log.Fields{"flow": flow})
+
+			device.ConfiguredVlanForDeviceFlows.Remove(VnetKey(vnet.SVlan, vnet.CVlan, 0))
+		}
+		return true
+	}
+	va.DevicesDisc.Range(delflows)
+}
+
+// DeleteDevFlowForDevice to delete icmpv6 flow for device
+func (va *VoltApplication) DeleteDevFlowForDevice(device *VoltDevice) {
+	logger.Infow(ctx, "DeleteDevFlowForDevice", log.Fields{"Device": device})
+	delicmpv6 := func(key, value interface{}) bool {
+		vnet := value.(*VoltVnet)
+		if vnetListIntf, ok := device.ConfiguredVlanForDeviceFlows.Get(VnetKey(vnet.SVlan, vnet.CVlan, 0)); ok {
+			vnetList := vnetListIntf.(*util.ConcurrentMap)
+			vnetList.Remove(vnet.Name)
+			device.ConfiguredVlanForDeviceFlows.Set(VnetKey(vnet.SVlan, vnet.CVlan, 0), vnetList)
+			if vnetList.Length() != 0 {
+				logger.Warnw(ctx, "Similar VNet associated to diff service. Not removing ICMPv6 flow", log.Fields{"SVlan": vnet.SVlan, "CVlan": vnet.CVlan, "vnetList-len": vnetList.Length()})
+				return true
+			}
+		} else {
+			logger.Warnw(ctx, "ICMPv6 Flow map entry not found for Vnet", log.Fields{"Vnet": vnet.VnetConfig})
+			return true
+		}
+		nniPortID, err := va.GetPortID(device.NniPort)
+		if err != nil {
+			logger.Errorw(ctx, "Delete ICMPv6 Failed - Failed to get NNI Port Id", log.Fields{"Port": device.NniPort, "Reason": err.Error})
+		}
+		flow := BuildICMPv6Flow(nniPortID, vnet)
+		flow.ForceAction = true
+		err = vnet.RemoveFlows(device, flow)
+		if err != nil {
+			logger.Warnw(ctx, "De-Configuring ICMPv6 Flow for device failed ", log.Fields{"Device": device.Name, "err": err})
+			return true
+		}
+
+		flow = BuildDSArpFlow(nniPortID, vnet)
+		flow.ForceAction = true
+		err = vnet.RemoveFlows(device, flow)
+		if err != nil {
+			logger.Warnw(ctx, "De-Configuring ARP Flow for device failed ", log.Fields{"Device": device.Name, "err": err})
+			return true
+		}
+
+		device.ConfiguredVlanForDeviceFlows.Remove(VnetKey(vnet.SVlan, vnet.CVlan, 0))
+		logger.Infow(ctx, "ICMP Flow Delete Added to Queue", log.Fields{"flow": flow})
+		return true
+	}
+	va.VnetsByName.Range(delicmpv6)
+	logger.Debugw(ctx, "De-Configuring ICMPv6 Group for device ", log.Fields{"Device": device.Name})
+	err := ProcessIcmpv6McGroup(device.Name, true)
+	if err != nil {
+		logger.Warnw(ctx, "De-Configuring ICMPv6 Group on device failed ", log.Fields{"Device": device.Name, "err": err})
+		return
+	}
+}
+
+// DeleteDevFlowForVlanFromDevice to delete icmpv6 flow for vlan from device
+func (va *VoltApplication) DeleteDevFlowForVlanFromDevice(vnet *VoltVnet, deviceSerialNum string) {
+	logger.Infow(ctx, "DeleteDevFlowForVlanFromDevice", log.Fields{"Device-serialNum": deviceSerialNum, "SVlan": vnet.SVlan, "CVlan": vnet.CVlan})
+	delflows := func(key interface{}, value interface{}) bool {
+		device := value.(*VoltDevice)
+		if device.SerialNum != deviceSerialNum {
+			return true
+		}
+		if vnetListIntf, ok := device.ConfiguredVlanForDeviceFlows.Get(VnetKey(vnet.SVlan, vnet.CVlan, 0)); ok {
+			vnetList := vnetListIntf.(*util.ConcurrentMap)
+			vnetList.Remove(vnet.Name)
+			device.ConfiguredVlanForDeviceFlows.Set(VnetKey(vnet.SVlan, vnet.CVlan, 0), vnetList)
+			if vnetList.Length() != 0 {
+				logger.Warnw(ctx, "Similar VNet associated to diff service. Not removing ICMPv6 flow", log.Fields{"SVlan": vnet.SVlan, "CVlan": vnet.CVlan, "vnetList-len": vnetList.Length()})
+				return true
+			}
+		} else if !vgcRebooted && len(vnet.DevicesList) != 0 {
+			// Return only in-case of non-reboot/delete scenario. Else, the flows need to be force removed
+			// DeviceList check is there to avoid dangling flow in-case of pod restart during service de-activation.
+			// The step will be as follow:
+			// 1. Deact Service
+			// 2. Pod Reboot
+			// 3. Pending Delete Service triggered
+			// 4. Del Service Ind followed by DelVnet req from NB
+			// 5. If Vlan status response is awaited, the ConfiguredVlanForDeviceFlows cache will not have flow info
+			// hence the flow will not be cleared
+			logger.Warnw(ctx, "Dev Flow map entry not found for Vnet", log.Fields{"PodReboot": vgcRebooted, "VnetDeleteInProgress": vnet.DeleteInProgress})
+			return true
+		}
+		if portID, err := va.GetPortID(device.NniPort); err == nil {
+			if state, _ := cntlr.GetController().GetPortState(device.Name, device.NniPort); state != cntlr.PortStateUp {
+				logger.Warnw(ctx, "Skipping ICMPv6 Flow Deletion - Port Down", log.Fields{"Device": device})
+				return false
+			}
+			flow := BuildICMPv6Flow(portID, vnet)
+			flow.ForceAction = true
+			if err := vnet.RemoveFlows(device, flow); err != nil {
+				logger.Warnw(ctx, "Delete Flow Failed", log.Fields{"Device": device, "Flow": flow, "Error": err})
+			}
+			logger.Infow(ctx, "ICMP Flow Delete Added to Queue", log.Fields{"flow": flow})
+
+			flow = BuildDSArpFlow(portID, vnet)
+			flow.ForceAction = true
+			if err := vnet.RemoveFlows(device, flow); err != nil {
+				logger.Warnw(ctx, "Delete Flow Failed", log.Fields{"Device": device, "Flow": flow, "Error": err})
+			}
+			logger.Infow(ctx, "ARP Flow Delete Added to Queue", log.Fields{"flow": flow})
+			device.ConfiguredVlanForDeviceFlows.Remove(VnetKey(vnet.SVlan, vnet.CVlan, 0))
+		}
+		return false
+	}
+	va.DevicesDisc.Range(delflows)
+}
+
+// BuildICMPv6Flow to Build DS flow for ICMPv6
+func BuildICMPv6Flow(inport uint32, vnet *VoltVnet) *of.VoltFlow {
+	logger.Info(ctx, "Building ICMPv6 MC Flow", log.Fields{"SVlan": vnet.SVlan, "CVlan": vnet.CVlan})
+	flow := &of.VoltFlow{}
+	flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
+	subFlow := of.NewVoltSubFlow()
+
+	subFlow.SetICMPv6Match()
+	subFlow.SetMatchVlan(vnet.SVlan)
+	subFlow.SetInPort(inport)
+	subFlow.SetPopVlan()
+	subFlow.SetOutGroup(ICMPv6ArpGroupID)
+	subFlow.Cookie = uint64(vnet.CVlan)<<48 | uint64(vnet.SVlan)<<32 | of.IgmpFlowMask | of.DsFlowMask
+	subFlow.Priority = of.McFlowPriority
+	var metadata uint64
+	if vnet.VlanControl == None {
+		metadata = uint64(ONUCVlan)<<32 | uint64(vnet.CVlan)
+	} else {
+		metadata = uint64(vnet.VlanControl)<<32 | uint64(vnet.CVlan)
+	}
+	subFlow.SetTableMetadata(metadata)
+	metadata = uint64(vnet.setPbitRemarking())
+
+	logger.Infow(ctx, "ICMPv6 Pbit Remarking", log.Fields{"RemarkPbit": metadata})
+	subFlow.SetWriteMetadata(metadata)
+	flow.SubFlows[subFlow.Cookie] = subFlow
+	return flow
+}
+
+//BuildDSArpFlow Builds DS flow for ARP
+func BuildDSArpFlow(inport uint32, vnet *VoltVnet) *of.VoltFlow {
+	logger.Info(ctx, "Building ARP MC Flow", log.Fields{"SVlan": vnet.SVlan, "CVlan": vnet.CVlan})
+
+	flow := &of.VoltFlow{}
+	flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
+	subFlow := of.NewVoltSubFlow()
+
+	BcastMAC, _ := net.ParseMAC("FF:FF:FF:FF:FF:FF")
+	subFlow.SetArpMatch()
+	subFlow.SetMatchDstMac(BcastMAC)
+	subFlow.SetMatchVlan(vnet.SVlan)
+	subFlow.SetInPort(inport)
+	subFlow.SetPopVlan()
+	subFlow.SetOutGroup(ICMPv6ArpGroupID)
+
+	subFlow.Cookie = uint64(vnet.CVlan)<<48 | uint64(vnet.SVlan)<<32 | of.DsArpFlowMask | of.DsFlowMask
+	subFlow.Priority = of.McFlowPriority
+
+	var metadata uint64
+	if vnet.VlanControl == None {
+		metadata = uint64(ONUCVlan)<<32 | uint64(vnet.CVlan)
+	} else {
+		metadata = uint64(vnet.VlanControl)<<32 | uint64(vnet.CVlan)
+	}
+	subFlow.SetTableMetadata(metadata)
+	metadata = uint64(vnet.setPbitRemarking())
+	subFlow.SetWriteMetadata(metadata)
+
+	flow.SubFlows[subFlow.Cookie] = subFlow
+	logger.Infow(ctx, "ARP Pbit Remarking", log.Fields{"RemarkPbit": metadata})
+	return flow
+}
+
+// setPbitRemarking to set Pbit remarking
+func (vv *VoltVnet) setPbitRemarking() uint32 {
+
+	// 	                              Remarkable
+	// 	         Remarked Pbit          Pbit
+	// |-----------------------------| |------|
+	// |7| |6| |5| |4| |3| |2| |1| |0| 76543210
+	// 000 000 000 000 000 000 000 000 00000000
+
+	// Eg:
+	// For 6:3 & 7:1
+	// 001 011 000 000 000 000 000 000 11000000
+
+	var remarkable uint8
+	var remarked uint32
+	for refPbit, remarkPbit := range vv.CtrlPktPbitRemark {
+		remarkable = remarkable | 1<<refPbit
+		remarked = remarked | uint32(remarkPbit)<<(refPbit*3)
+	}
+	return remarked<<8 | uint32(remarkable)
+}
+
+// ProcessIcmpv6McGroup to add icmpv6 multicast group
+func ProcessIcmpv6McGroup(device string, delete bool) error {
+
+	logger.Info(ctx, "Creating ICMPv6 MC Group")
+	va := GetApplication()
+	vd := va.GetDevice(device)
+	group := &of.Group{}
+	group.GroupID = ICMPv6ArpGroupID
+	group.Device = device
+	if delete {
+		if !vd.icmpv6GroupAdded {
+			logger.Info(ctx, "ICMPv6 MC Group is already deleted. Ignoring  icmpv6 group Delete")
+			return nil //TODO
+		}
+		vd.icmpv6GroupAdded = false
+		group.Command = of.GroupCommandDel
+		group.ForceAction = true
+	} else {
+		if vd.icmpv6GroupAdded {
+			logger.Info(ctx, "ICMPv6 MC Group is already added. Ignoring icmpv6 group Add")
+			return nil //TODO
+		}
+		vd.icmpv6GroupAdded = true
+		group.Command = of.GroupCommandAdd
+		receivers := GetApplication().GetIcmpv6Receivers(device)
+		group.Buckets = append(group.Buckets, receivers...)
+	}
+	logger.Infow(ctx, "ICMPv6 MC Group Action", log.Fields{"Device": device, "Delete": delete})
+	port, _ := GetApplication().GetNniPort(device)
+	err := cntlr.GetController().GroupUpdate(port, device, group)
+	return err
+}
+
+//isVlanMatching - checks is vlans matches with vpv based on vlan control
+func (vpv *VoltPortVnet) isVlanMatching(cvlan of.VlanType, svlan of.VlanType) bool {
+
+	switch vpv.VlanControl {
+	case ONUCVlanOLTSVlan,
+		OLTCVlanOLTSVlan:
+		if vpv.SVlan == svlan && vpv.CVlan == cvlan {
+			return true
+		}
+	case ONUCVlan,
+		OLTSVlan,
+		None:
+		if vpv.SVlan == svlan {
+			return true
+		}
+	default:
+		logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vpv.VlanControl})
+	}
+	return false
+}
+
+//PushFlows - Triggers flow addition after registering for flow indication event
+func (vpv *VoltPortVnet) PushFlows(device *VoltDevice, flow *of.VoltFlow) error {
+
+	for cookie := range flow.SubFlows {
+		cookie := strconv.FormatUint(cookie, 10)
+		fe := &FlowEvent{
+			eType:     EventTypeControlFlowAdded,
+			cookie:    cookie,
+			eventData: vpv,
+		}
+		device.RegisterFlowAddEvent(cookie, fe)
+	}
+	return cntlr.GetController().AddFlows(vpv.Port, device.Name, flow)
+}
+
+//FlowInstallFailure - Process flow failure indication and triggers HSIA failure for all associated services
+func (vpv *VoltPortVnet) FlowInstallFailure(cookie string, errorCode uint32, errReason string) {
+
+	sendFlowFailureInd := func(key, value interface{}) bool {
+		//svc := value.(*VoltService)
+		//TODO-COMM: svc.triggerServiceFailureInd(errorCode, errReason)
+		return true
+	}
+	logger.Errorw(ctx, "Control Flow Add Failure Notification", log.Fields{"uniPort": vpv.Port, "Cookie": cookie, "ErrorCode": errorCode, "ErrorReason": errReason})
+	vpv.services.Range(sendFlowFailureInd)
+}
+
+//RemoveFlows - Triggers flow deletion after registering for flow indication event
+func (vpv *VoltPortVnet) RemoveFlows(device *VoltDevice, flow *of.VoltFlow) error {
+
+	vpv.PendingFlowLock.Lock()
+	defer vpv.PendingFlowLock.Unlock()
+
+	for cookie := range flow.SubFlows {
+		cookie := strconv.FormatUint(cookie, 10)
+		fe := &FlowEvent{
+			eType:     EventTypeControlFlowRemoved,
+			device:    device.Name,
+			cookie:    cookie,
+			eventData: vpv,
+		}
+		device.RegisterFlowDelEvent(cookie, fe)
+		vpv.PendingDeleteFlow[cookie] = true
+	}
+	return cntlr.GetController().DelFlows(vpv.Port, device.Name, flow)
+}
+
+//CheckAndDeleteVpv - remove VPV from DB is there are no pending flows to be removed
+func (vpv *VoltPortVnet) CheckAndDeleteVpv() {
+	vpv.PendingFlowLock.RLock()
+	defer vpv.PendingFlowLock.RUnlock()
+	if !vpv.DeleteInProgress {
+		return
+	}
+	if len(vpv.PendingDeleteFlow) == 0 && !vpv.FlowsApplied {
+		logger.Infow(ctx, "All Flows removed for VPV. Triggering VPV Deletion from DB", log.Fields{"VPV Port": vpv.Port, "Device": vpv.Device, "Vnet": vpv.VnetName})
+		vpv.DelFromDb()
+		logger.Infow(ctx, "Deleted VPV from DB/Cache successfully", log.Fields{"VPV Port": vpv.Port, "Device": vpv.Device, "Vnet": vpv.VnetName})
+	}
+}
+
+//FlowRemoveSuccess - Process flow success indication
+func (vpv *VoltPortVnet) FlowRemoveSuccess(cookie string, device string) {
+	vpv.PendingFlowLock.Lock()
+	logger.Infow(ctx, "VPV Flow Remove Success Notification", log.Fields{"Port": vpv.Port, "Cookie": cookie, "Device": device})
+
+	delete(vpv.PendingDeleteFlow, cookie)
+	vpv.PendingFlowLock.Unlock()
+	vpv.CheckAndDeleteVpv()
+	vpv.WriteToDb()
+}
+
+//FlowRemoveFailure - Process flow failure indication and triggers Del HSIA failure for all associated services
+func (vpv *VoltPortVnet) FlowRemoveFailure(cookie string, device string, errorCode uint32, errReason string) {
+	vpv.PendingFlowLock.Lock()
+
+	logger.Errorw(ctx, "VPV Flow Remove Failure Notification", log.Fields{"Port": vpv.Port, "Cookie": cookie, "ErrorCode": errorCode, "ErrorReason": errReason, "Device": device})
+
+	sendFlowFailureInd := func(key, value interface{}) bool {
+		svc := value.(*VoltService)
+		svc.triggerServiceFailureInd(errorCode, errReason)
+		return true
+	}
+	logger.Errorw(ctx, "Control Flow Del Failure Notification", log.Fields{"uniPort": vpv.Port, "Cookie": cookie, "ErrorCode": errorCode, "ErrorReason": errReason})
+	vpv.services.Range(sendFlowFailureInd)
+
+	if vpv.DeleteInProgress {
+		delete(vpv.PendingDeleteFlow, cookie)
+		vpv.PendingFlowLock.Unlock()
+		vpv.CheckAndDeleteVpv()
+	} else {
+		vpv.PendingFlowLock.Unlock()
+		vpv.WriteToDb()
+	}
+}
+
+//RemoveFlows - Triggers flow deletion after registering for flow indication event
+func (vv *VoltVnet) RemoveFlows(device *VoltDevice, flow *of.VoltFlow) error {
+
+	vv.VnetLock.Lock()
+	defer vv.VnetLock.Unlock()
+
+	var flowMap map[string]bool
+	var ok bool
+
+	for cookie := range flow.SubFlows {
+		cookie := strconv.FormatUint(cookie, 10)
+		fe := &FlowEvent{
+			eType:     EventTypeDeviceFlowRemoved,
+			device:    device.Name,
+			cookie:    cookie,
+			eventData: vv,
+		}
+		device.RegisterFlowDelEvent(cookie, fe)
+		if flowMap, ok = vv.PendingDeleteFlow[device.Name]; !ok {
+			flowMap = make(map[string]bool)
+		}
+		flowMap[cookie] = true
+		vv.PendingDeleteFlow[device.Name] = flowMap
+	}
+	vv.WriteToDb()
+	return cntlr.GetController().DelFlows(device.NniPort, device.Name, flow)
+}
+
+//CheckAndDeleteVnet - remove Vnet from DB is there are no pending flows to be removed
+func (vv *VoltVnet) CheckAndDeleteVnet(device string) {
+	if !vv.DeleteInProgress {
+		return
+	}
+	vv.VnetPortLock.RLock()
+	if len(vv.PendingDeleteFlow[device]) == 0 && !vv.isAssociatedPortsPresent() {
+		logger.Warnw(ctx, "Deleting Vnet : All flows removed", log.Fields{"Name": vv.Name, "AssociatedPorts": vv.AssociatedPorts, "Device": device})
+		GetApplication().deleteVnetConfig(vv)
+		_ = db.DelVnet(vv.Name)
+		logger.Infow(ctx, "Deleted Vnet from DB/Cache successfully", log.Fields{"Device": device, "Vnet": vv.Name})
+	} else {
+		logger.Warnw(ctx, "Skipping Del Vnet", log.Fields{"Name": vv.Name, "AssociatedPorts": vv.AssociatedPorts, "PendingDelFlows": vv.PendingDeleteFlow[device]})
+	}
+	vv.VnetPortLock.RUnlock()
+}
+
+//FlowRemoveSuccess - Process flow success indication
+func (vv *VoltVnet) FlowRemoveSuccess(cookie string, device string) {
+	vv.VnetLock.Lock()
+	defer vv.VnetLock.Unlock()
+
+	logger.Infow(ctx, "Vnet Flow Remove Success Notification", log.Fields{"VnetProfile": vv.Name, "Cookie": cookie, "Device": device})
+
+	if _, ok := vv.PendingDeleteFlow[device]; ok {
+		delete(vv.PendingDeleteFlow[device], cookie)
+	}
+
+	//Check and update success for pending disable request
+	if d := GetApplication().GetDevice(device); d != nil {
+		_, present := d.ConfiguredVlanForDeviceFlows.Get(VnetKey(vv.SVlan, vv.CVlan, 0))
+		if !present && len(vv.PendingDeleteFlow[device]) == 0 {
+			vv.CheckAndDeleteVnet(device)
+		}
+	}
+	vv.WriteToDb()
+}
+
+//FlowRemoveFailure - Process flow failure indication
+func (vv *VoltVnet) FlowRemoveFailure(cookie string, device string, errorCode uint32, errReason string) {
+
+	vv.VnetLock.Lock()
+	defer vv.VnetLock.Unlock()
+
+	if flowMap, ok := vv.PendingDeleteFlow[device]; ok {
+		if _, ok := flowMap[cookie]; ok {
+			logger.Errorw(ctx, "Device Flow Remove Failure Notification", log.Fields{"Vnet": vv.Name, "Cookie": cookie, "ErrorCode": errorCode, "ErrorReason": errReason, "Device": device})
+
+			if vv.DeleteInProgress {
+				delete(vv.PendingDeleteFlow[device], cookie)
+				vv.CheckAndDeleteVnet(device)
+			}
+			return
+		}
+	}
+	logger.Errorw(ctx, "Device Flow Remove Failure Notification for Unknown cookie", log.Fields{"Vnet": vv.Name, "Cookie": cookie, "ErrorCode": errorCode, "ErrorReason": errReason})
+}
+
+//IgmpFlowInstallFailure - Process flow failure indication and triggers HSIA failure for Igmp enabled services
+func (vpv *VoltPortVnet) IgmpFlowInstallFailure(cookie string, errorCode uint32, errReason string) {
+
+	//Note: Current implementation supports only for single service with Igmp Enabled for a subscriber
+	//When multiple Igmp-suported service enabled, comment "return false"
+
+	sendFlowFailureInd := func(key, value interface{}) bool {
+		svc := value.(*VoltService)
+		if svc.IgmpEnabled {
+			svc.triggerServiceFailureInd(errorCode, errReason)
+			return false
+		}
+		return true
+	}
+	logger.Errorw(ctx, "US IGMP Flow Failure Notification", log.Fields{"uniPort": vpv.Port, "Cookie": cookie, "ErrorCode": errorCode, "ErrorReason": errReason})
+	vpv.services.Range(sendFlowFailureInd)
+}
+
+// GetMatchingMcastService to get matching multicast service
+func (va *VoltApplication) GetMatchingMcastService(port string, device string, cvlan of.VlanType) *VoltService {
+
+	var service *VoltService
+	dIntf, ok := va.DevicesDisc.Load(device)
+	if !ok {
+		return nil
+	}
+	d := dIntf.(*VoltDevice)
+
+	// If the port is NNI port, the services dont exist on it. The svc then
+	// must be obtained from a different context and is not included here
+	if port == d.NniPort {
+		return nil
+	}
+
+	// This is an access port and the port should have all the associated
+	// services which can be uniquely identified by the VLANs in the packet
+	vnets, ok := va.VnetsByPort.Load(port)
+
+	if !ok {
+		logger.Debugw(ctx, "No Vnets for port", log.Fields{"Port": port})
+		return nil
+	}
+	logger.Debugw(ctx, "Matching for VLANs", log.Fields{"VLANs": cvlan})
+	getMcastService := func(key, value interface{}) bool {
+		srv := value.(*VoltService)
+		if srv.IgmpEnabled {
+			service = srv
+
+			//TODO: Current implementation supports only for single service with Igmp Enabled
+			//FIX-ME:  When multiple service suports Igmp, update of logic required
+			return false
+		}
+		return true
+	}
+
+	for _, vpv := range vnets.([]*VoltPortVnet) {
+		if vpv.CVlan == cvlan {
+			vpv.services.Range(getMcastService)
+			if service != nil {
+				break
+			}
+		}
+	}
+	return service
+}
+
+//TriggerAssociatedFlowDelete - Re-trigger delete for pending delete flows
+func (vv *VoltVnet) TriggerAssociatedFlowDelete(device string) bool {
+	vv.VnetLock.Lock()
+	cookieList := []uint64{}
+	flowMap := vv.PendingDeleteFlow[device]
+
+	for cookie := range flowMap {
+		cookieList = append(cookieList, convertToUInt64(cookie))
+	}
+	vv.VnetLock.Unlock()
+
+	if len(cookieList) == 0 {
+		return false
+	}
+
+	for _, cookie := range cookieList {
+		if vd := GetApplication().GetDevice(device); vd != nil {
+			flow := &of.VoltFlow{}
+			flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
+			subFlow := of.NewVoltSubFlow()
+			subFlow.Cookie = cookie
+			flow.SubFlows[cookie] = subFlow
+			logger.Infow(ctx, "Retriggering Vnet Delete Flow", log.Fields{"Device": device, "Vnet": vv.Name, "Cookie": cookie})
+			if err := vv.RemoveFlows(vd, flow); err != nil {
+				logger.Warnw(ctx, "Vnet Delete Flow Failed", log.Fields{"Device": device, "Vnet": vv.Name, "Cookie": cookie, "Error": err})
+			}
+		}
+	}
+	return true
+}