[VOL-5458] - Multi NNI support in VGC

Change-Id: I4ed19bf43a5594109a16397da94c56cda87c69f0
Signed-off-by: Sridhar Ravindra <sridhar.ravindra@radisys.com>
diff --git a/VERSION b/VERSION
index c946ee6..1180819 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.1.6
+0.1.7
diff --git a/internal/pkg/application/application.go b/internal/pkg/application/application.go
index f0fa05e..4f390fd 100644
--- a/internal/pkg/application/application.go
+++ b/internal/pkg/application/application.go
@@ -160,6 +160,7 @@
 	Type                     VoltPortType
 	State                    PortState
 	ChannelPerSubAlarmRaised bool
+	NniDhcpTrapFlowAdded     bool
 }
 
 // NewVoltPort : Constructor for the port.
@@ -209,26 +210,20 @@
 	MigratingServices            *util.ConcurrentMap //<vnetID,<RequestID, MigrateServicesRequest>>
 	VpvsBySvlan                  *util.ConcurrentMap // map[svlan]map[vnet_port]*VoltPortVnet
 	ConfiguredVlanForDeviceFlows *util.ConcurrentMap //map[string]map[string]bool
-
-	IgmpDsFlowAppliedForMvlan map[uint16]bool
-
-	Ports                sync.Map
-	VlanPortStatus       sync.Map
-	ActiveChannelsPerPon sync.Map // [PonPortID]*PonPortCfg
-	PonPortList          sync.Map // [PonPortID]map[string]string
-
-	State        controller.DeviceState
-	SouthBoundID string
-	NniPort      string
-	Name         string
-	SerialNum    string
-
-	ActiveChannelCountLock sync.Mutex // This lock is used to update ActiveIGMPChannels
-
-	NniDhcpTrapVid of.VlanType
-
-	GlobalDhcpFlowAdded bool
-	icmpv6GroupAdded    bool
+	IgmpDsFlowAppliedForMvlan    map[uint16]bool
+	Ports                        sync.Map
+	VlanPortStatus               sync.Map
+	ActiveChannelsPerPon         sync.Map // [PonPortID]*PonPortCfg
+	PonPortList                  sync.Map // [PonPortID]map[string]string
+	State                        controller.DeviceState
+	SouthBoundID                 string
+	Name                         string
+	SerialNum                    string
+	NniPort                      []string
+	ActiveChannelCountLock       sync.Mutex // This lock is used to update ActiveIGMPChannels
+	NniDhcpTrapVid               of.VlanType
+	GlobalDhcpFlowAdded          bool
+	icmpv6GroupAdded             bool
 }
 
 type VoltDevInterface interface {
@@ -241,7 +236,7 @@
 	d.Name = name
 	d.SouthBoundID = southBoundID
 	d.State = controller.DeviceStateDOWN
-	d.NniPort = ""
+	d.NniPort = make([]string, 0)
 	d.SouthBoundID = southBoundID
 	d.SerialNum = slno
 	d.icmpv6GroupAdded = false
@@ -339,12 +334,21 @@
 	va.AggActiveChannelsCountPerSub(d.Name, port, p)
 	d.Ports.Store(port, p)
 	if util.IsNniPort(id) {
-		d.NniPort = port
+		d.NniPort = append(d.NniPort, port)
 	}
 	addPonPortFromUniPort(p)
 	return p
 }
 
+func (d *VoltDevice) IsPortNni(port string) bool {
+	for _, nniPort := range d.NniPort {
+		if nniPort == port {
+			return true
+		}
+	}
+	return false
+}
+
 // GetPort to get port information from the device.
 func (d *VoltDevice) GetPort(port string) *VoltPort {
 	logger.Debugw(ctx, "Get Port", log.Fields{"Port": port})
@@ -354,7 +358,7 @@
 	return nil
 }
 
-// GetPortByPortID to get port information from the device.
+// GetPortNameFromPortID to get port information from the device.
 func (d *VoltDevice) GetPortNameFromPortID(portID uint32) string {
 	logger.Debugw(ctx, "Get Port Name from the device", log.Fields{"PortID": portID})
 	portName := ""
@@ -368,6 +372,20 @@
 	return portName
 }
 
+// GetPortIDFromPortName to get port information from the device.
+func (d *VoltDevice) GetPortIDFromPortName(portName string) uint32 {
+	logger.Debugw(ctx, "Get Port ID from the device", log.Fields{"PortName": portName})
+	var portID uint32
+	d.Ports.Range(func(key, value interface{}) bool {
+		vp := value.(*VoltPort)
+		if vp.Name == portName {
+			portID = vp.ID
+		}
+		return true
+	})
+	return portID
+}
+
 // DelPort to delete port from the device
 func (d *VoltDevice) DelPort(port string) {
 	logger.Debugw(ctx, "Delete Port from the device", log.Fields{"Port": port})
@@ -399,7 +417,7 @@
 
 		for _, vpv := range vnets.([]*VoltPortVnet) {
 			vpv.VpvLock.Lock()
-			vpv.PortUpInd(cntx, d, port)
+			vpv.PortUpInd(cntx, d, port, "")
 			vpv.VpvLock.Unlock()
 		}
 		return true
@@ -419,7 +437,7 @@
 type VoltAppInterface interface {
 	AddVnet(cntx context.Context, cfg VnetConfig, oper *VnetOper) error
 	AddService(cntx context.Context, cfg VoltServiceCfg, oper *VoltServiceOper) error
-	AddDeviceConfig(cntx context.Context, serialNum, hardwareIdentifier, nasID, ipAddress, uplinkPort string, nniDhcpTrapID uint16) error
+	AddDeviceConfig(cntx context.Context, serialNum, hardwareIdentifier, nasID, ipAddress, uplinkPort string, nniDhcpTrapID uint16, nniPorts []string) error
 	GetFlowProvisionStatus(portNo string) FlowProvisionStatus
 	DelServiceWithPrefix(cntx context.Context, prefix string) error
 	GetDevice(device string) *VoltDevice
@@ -499,12 +517,13 @@
 }
 
 type DeviceConfig struct {
-	SerialNumber       string `json:"id"`
-	HardwareIdentifier string `json:"hardwareIdentifier"`
-	IPAddress          string `json:"ipAddress"`
-	UplinkPort         string `json:"uplinkPort"`
-	NasID              string `json:"nasId"`
-	NniDhcpTrapVid     uint16 `json:"nniDhcpTrapVid"`
+	SerialNumber       string   `json:"id"`
+	HardwareIdentifier string   `json:"hardwareIdentifier"`
+	IPAddress          string   `json:"ipAddress"`
+	UplinkPort         string   `json:"uplinkPort"`
+	NasID              string   `json:"nasId"`
+	NniPorts           []string `json:"nniPorts"`
+	NniDhcpTrapVid     uint16   `json:"nniDhcpTrapVid"`
 }
 
 // PonPortCfg contains NB port config and activeIGMPChannels count
@@ -602,7 +621,7 @@
 			continue
 		}
 		logger.Debugw(ctx, "Retrieved device config", log.Fields{"Device Config": devConfig})
-		if err := va.AddDeviceConfig(cntx, devConfig.SerialNumber, devConfig.HardwareIdentifier, devConfig.NasID, devConfig.IPAddress, devConfig.UplinkPort, devConfig.NniDhcpTrapVid); err != nil {
+		if err := va.AddDeviceConfig(cntx, devConfig.SerialNumber, devConfig.HardwareIdentifier, devConfig.NasID, devConfig.IPAddress, devConfig.UplinkPort, devConfig.NniDhcpTrapVid, devConfig.NniPorts); err != nil {
 			logger.Warnw(ctx, "Add device config failed", log.Fields{"DeviceConfig": devConfig, "Error": err})
 		}
 	}
@@ -621,7 +640,7 @@
 	return nil
 }
 
-func (va *VoltApplication) AddDeviceConfig(cntx context.Context, serialNum, hardwareIdentifier, nasID, ipAddress, uplinkPort string, nniDhcpTrapID uint16) error {
+func (va *VoltApplication) AddDeviceConfig(cntx context.Context, serialNum, hardwareIdentifier, nasID, ipAddress, uplinkPort string, nniDhcpTrapID uint16, nniPorts []string) error {
 	logger.Debugw(ctx, "Received Add device config", log.Fields{"SerialNumber": serialNum, "HardwareIdentifier": hardwareIdentifier, "NasID": nasID, "IPAddress": ipAddress, "UplinkPort": uplinkPort, "NniDhcpTrapID": nniDhcpTrapID})
 	var dc *DeviceConfig
 
@@ -632,6 +651,7 @@
 		UplinkPort:         uplinkPort,
 		IPAddress:          ipAddress,
 		NniDhcpTrapVid:     nniDhcpTrapID,
+		NniPorts:           nniPorts,
 	}
 	va.DevicesConfig.Store(serialNum, deviceConfig)
 	err := dc.WriteDeviceConfigToDb(cntx, serialNum, deviceConfig)
@@ -1139,13 +1159,29 @@
 // port which is a result of protection methods applied.
 func (va *VoltApplication) GetNniPort(device string) (string, error) {
 	logger.Debugw(ctx, "NNI Get Ind", log.Fields{"device": device})
-	va.portLock.Lock()
-	defer va.portLock.Unlock()
 	d, ok := va.DevicesDisc.Load(device)
 	if !ok {
 		return "", errors.New("device doesn't exist")
 	}
-	return d.(*VoltDevice).NniPort, nil
+	devConfig := va.GetDeviceConfig(d.(*VoltDevice).SerialNum)
+	if devConfig == nil {
+		return "", fmt.Errorf("device config not found for serial number %s", d.(*VoltDevice).SerialNum)
+	}
+	if len(d.(*VoltDevice).NniPort) > 0 {
+		for _, nniPort := range d.(*VoltDevice).NniPort {
+			nniPortID, err := GetApplication().GetPortID(nniPort)
+			if err != nil {
+				logger.Errorw(ctx, "Error getting port ID by port Name", log.Fields{"Error": err})
+				continue
+			}
+			if devConfig.UplinkPort == strconv.Itoa(int(nniPortID)) {
+				logger.Debugw(ctx, "NNI port configured from NB", log.Fields{"NB NNI Port": devConfig.UplinkPort, "SB NNI Ports": d.(*VoltDevice).NniPort})
+				return nniPort, nil // Match found
+			}
+		}
+	}
+	// If no matching NNI port is found, return an error
+	return "", errors.New("nni port doesn't exist")
 }
 
 // NniDownInd process for Nni down indication.
@@ -1215,7 +1251,11 @@
 func (va *VoltApplication) ProcessIgmpDSFlowForMvlan(cntx context.Context, d *VoltDevice, mvp *MvlanProfile, addFlow bool) {
 	logger.Debugw(ctx, "Process IGMP DS Flows for MVlan", log.Fields{"device": d.Name, "Mvlan": mvp.Mvlan, "addFlow": addFlow})
 	portState := false
-	p := d.GetPort(d.NniPort)
+	nniPort, err := va.GetNniPort(d.Name)
+	if err != nil {
+		logger.Errorw(ctx, "Error gettin NNI port", log.Fields{"Error": err})
+	}
+	p := d.GetPort(nniPort)
 	if p != nil && p.State == PortStateUp {
 		portState = true
 	}
@@ -1438,9 +1478,12 @@
 
 	if p.Type == VoltPortTypeNni {
 		logger.Debugw(ctx, "Received NNI Port Ind: UP", log.Fields{"Device": device, "PortName": port, "PortId": p.ID})
-		//va.PushDevFlowForDevice(d)
-		//Build Igmp TrapFlowRule
-		//va.ProcessIgmpDSFlowForDevice(d, true)
+		if d.NniDhcpTrapVid == 1 {
+			err := va.AddDefaultDhcpTrapFlow(cntx, d, p)
+			if err != nil {
+				logger.Errorw(ctx, "Failed adding DHCP trap flow", log.Fields{"Device": device, "PortName": port, "PortId": p.ID, "error": err})
+			}
+		}
 	}
 	vpvs, ok := va.VnetsByPort.Load(port)
 	if !ok || nil == vpvs || len(vpvs.([]*VoltPortVnet)) == 0 {
@@ -1450,7 +1493,7 @@
 	}
 
 	// If NNI port is not UP, do not push Flows
-	if d.NniPort == "" {
+	if len(d.NniPort) == 0 {
 		logger.Warnw(ctx, "NNI port not UP. Not sending Port UP Ind for VPVs", log.Fields{"NNI": d.NniPort})
 		return
 	}
@@ -1458,12 +1501,12 @@
 	for _, vpv := range vpvs.([]*VoltPortVnet) {
 		vpv.VpvLock.Lock()
 		// If no service is activated drop the portUpInd
-		if vpv.IsServiceActivated(cntx) {
+		if ok, nniPort := vpv.IsServiceActivated(cntx); ok {
 			// Do not trigger indication for the vpv which is already removed from vpv list as
 			// part of service delete (during the lock wait duration)
 			// In that case, the services associated wil be zero
 			if vpv.servicesCount.Load() != 0 {
-				vpv.PortUpInd(cntx, d, port)
+				vpv.PortUpInd(cntx, d, port, nniPort)
 			}
 		} else {
 			// Service not activated, still attach device to service
@@ -1801,7 +1844,7 @@
 	if flow.FlowCount >= uint32(controller.GetController().GetMaxFlowRetryAttempt()) {
 		devConfig := va.GetDeviceConfig(devSerialNum)
 		if devConfig != nil {
-			portNo := util.GetUniPortFromFlow(devConfig.UplinkPort, flow)
+			portNo := util.GetUniPortFromFlow(devConfig.UplinkPort, devConfig.NniPorts, flow)
 			portName, err := va.GetPortName(portNo)
 			if err != nil {
 				logger.Warnw(ctx, "Error getting port name", log.Fields{"Reason": err.Error(), "PortID": portNo})
diff --git a/internal/pkg/application/application_test.go b/internal/pkg/application/application_test.go
index 3372266..5eef192 100644
--- a/internal/pkg/application/application_test.go
+++ b/internal/pkg/application/application_test.go
@@ -740,7 +740,7 @@
 		SerialNum:                    "SDX6320033",
 		SouthBoundID:                 "68580342-6b3e-57cb-9ea4-06125594e330",
 		State:                        controller.DeviceStateDOWN,
-		NniPort:                      "",
+		NniPort:                      make([]string, 0),
 		icmpv6GroupAdded:             false,
 		IgmpDsFlowAppliedForMvlan:    make(map[uint16]bool),
 		ConfiguredVlanForDeviceFlows: util.NewConcurrentMap(),
@@ -1308,6 +1308,7 @@
 		ipAddress          string
 		uplinkPort         string
 		nniDhcpTrapID      uint16
+		nniPorts           []string
 	}
 	dvcConfg := &DeviceConfig{
 		SerialNumber:       "SDX6320031",
@@ -1351,7 +1352,7 @@
 			dbintf := mocks.NewMockDBIntf(gomock.NewController(t))
 			db = dbintf
 			dbintf.EXPECT().PutDeviceConfig(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
-			if err := va.AddDeviceConfig(tt.args.cntx, tt.args.serialNum, tt.args.hardwareIdentifier, tt.args.nasID, tt.args.ipAddress, tt.args.uplinkPort, tt.args.nniDhcpTrapID); (err != nil) != tt.wantErr {
+			if err := va.AddDeviceConfig(tt.args.cntx, tt.args.serialNum, tt.args.hardwareIdentifier, tt.args.nasID, tt.args.ipAddress, tt.args.uplinkPort, tt.args.nniDhcpTrapID, tt.args.nniPorts); (err != nil) != tt.wantErr {
 				t.Errorf("VoltApplication.AddDeviceConfig() error = %v, wantErr %v", err, tt.wantErr)
 			}
 		})
@@ -1758,7 +1759,7 @@
 		Name:           "49686e2d-618f-4e8e-bca0-442ab850a63a",
 		SerialNum:      "SDX6320031",
 		NniDhcpTrapVid: 123,
-		NniPort:        "16777216",
+		NniPort:        []string{"16777216"},
 		SouthBoundID:   "49686e2d-618f-4e8e-bca0-442ab850a63a123",
 	}
 	nbd := &NbDevice{
@@ -1827,7 +1828,7 @@
 		Name:           "49686e2d-618f-4e8e-bca0-442ab850a63a",
 		SerialNum:      "SDX6320031",
 		NniDhcpTrapVid: 123,
-		NniPort:        "16777216",
+		NniPort:        []string{"16777216"},
 		SouthBoundID:   "49686e2d-618f-4e8e-bca0-442ab850a63a123",
 	}
 	tests := []struct {
@@ -1883,7 +1884,7 @@
 		Name:           "49686e2d-618f-4e8e-bca0-442ab850a63a",
 		SerialNum:      "SDX6320031",
 		NniDhcpTrapVid: 123,
-		NniPort:        "16777216",
+		NniPort:        []string{"16777216"},
 		SouthBoundID:   "49686e2d-618f-4e8e-bca0-442ab850a63a123",
 	}
 	tests := []struct {
@@ -1935,7 +1936,7 @@
 		Name:           "SDX6320031",
 		SerialNum:      "SDX6320031",
 		NniDhcpTrapVid: 123,
-		NniPort:        "16777216",
+		NniPort:        []string{"16777216"},
 		SouthBoundID:   "49686e2d-618f-4e8e-bca0-442ab850a63a123",
 	}
 	tests := []struct {
@@ -2000,7 +2001,7 @@
 		Name:           "SDX6320031",
 		SerialNum:      "SDX6320031",
 		NniDhcpTrapVid: 123,
-		NniPort:        "16777216",
+		NniPort:        []string{"16777216"},
 		SouthBoundID:   "49686e2d-618f-4e8e-bca0-442ab850a63a",
 	}
 	nbd := &NbDevice{
@@ -2076,7 +2077,7 @@
 		Name:                 "SDX6320031",
 		SerialNum:            "SDX6320031",
 		NniDhcpTrapVid:       123,
-		NniPort:              "16777216",
+		NniPort:              []string{"16777216"},
 		SouthBoundID:         "49686e2d-618f-4e8e-bca0-442ab850a63a",
 		ActiveChannelsPerPon: sync.Map{},
 	}
@@ -2181,7 +2182,7 @@
 		Name:                 "SDX6320031",
 		SerialNum:            "SDX6320031",
 		NniDhcpTrapVid:       123,
-		NniPort:              "16777216",
+		NniPort:              []string{"16777216"},
 		SouthBoundID:         "49686e2d-618f-4e8e-bca0-442ab850a63a",
 		ActiveChannelsPerPon: sync.Map{},
 	}
@@ -2258,7 +2259,7 @@
 		Name:           "SDX6320031",
 		SerialNum:      "SDX6320031",
 		NniDhcpTrapVid: 123,
-		NniPort:        "16777216",
+		NniPort:        []string{"16777216"},
 		SouthBoundID:   "49686e2d-618f-4e8e-bca0-442ab850a63a",
 	}
 	tests := []struct {
@@ -2302,7 +2303,7 @@
 		Name:           "SDX6320031",
 		SerialNum:      "SDX6320031",
 		NniDhcpTrapVid: 123,
-		NniPort:        "16777216",
+		NniPort:        []string{"16777216"},
 		SouthBoundID:   "49686e2d-618f-4e8e-bca0-442ab850a63a",
 	}
 	tests := []struct {
@@ -2399,7 +2400,7 @@
 		Name:           "SDX6320031",
 		SerialNum:      "SDX6320031",
 		NniDhcpTrapVid: 123,
-		NniPort:        "16777216",
+		NniPort:        []string{"16777216"},
 		SouthBoundID:   "49686e2d-618f-4e8e-bca0-442ab850a63a",
 		Ports:          sync.Map{},
 	}
@@ -2546,7 +2547,7 @@
 		Name:           "SDX6320031",
 		SerialNum:      "SDX6320031",
 		NniDhcpTrapVid: 123,
-		NniPort:        "16777472",
+		NniPort:        []string{"16777216"},
 		SouthBoundID:   "49686e2d-618f-4e8e-bca0-442ab850a63a",
 		Ports:          sync.Map{},
 		VpvsBySvlan:    util.NewConcurrentMap(),
@@ -2606,6 +2607,7 @@
 			voltPortVnet.services.Store("SDX6320031-1_SDX6320031-1-4096-2310-4096-65", voltServ)
 			voltapp := GetApplication()
 			voltapp.DevicesDisc.Store("SDX6320031", voltDev)
+			voltapp.DevicesConfig.Store("SDX6320031", &DeviceConfig{UplinkPort: "16777216"})
 			dbintf := mocks.NewMockDBIntf(gomock.NewController(t))
 			db = dbintf
 			dbintf.EXPECT().PutVpv(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
@@ -2624,7 +2626,7 @@
 		Name:           "SDX6320031",
 		SerialNum:      "SDX6320031",
 		NniDhcpTrapVid: 123,
-		NniPort:        "16777472",
+		NniPort:        []string{"16777216"},
 		SouthBoundID:   "49686e2d-618f-4e8e-bca0-442ab850a63a",
 		Ports:          sync.Map{},
 		VpvsBySvlan:    util.NewConcurrentMap(),
@@ -3137,7 +3139,7 @@
 		Name:           "49686e2d-618f-4e8e-bca0",
 		SerialNum:      "SDX6320031",
 		NniDhcpTrapVid: 123,
-		NniPort:        "16777472",
+		NniPort:        []string{"16777216"},
 		SouthBoundID:   "49686e2d-618f-4e8e-bca0-442ab850a63a",
 		Ports:          sync.Map{},
 		VpvsBySvlan:    util.NewConcurrentMap(),
@@ -3604,7 +3606,7 @@
 		SerialNum:                 "SDX6320031",
 		IgmpDsFlowAppliedForMvlan: mblan,
 		Ports:                     sync.Map{},
-		NniPort:                   "16777472",
+		NniPort:                   []string{"16777216"},
 	}
 	devicesList := make(map[string]OperInProgress)
 	devicesList["SDX6320030"] = opt82
diff --git a/internal/pkg/application/dhcprelay_test.go b/internal/pkg/application/dhcprelay_test.go
index 66edc57..d27ee8e 100644
--- a/internal/pkg/application/dhcprelay_test.go
+++ b/internal/pkg/application/dhcprelay_test.go
@@ -37,7 +37,7 @@
 		Name:         "11c3175b-50f3-4220-9555-93df733ded1d",
 		SerialNum:    "SDX6320031",
 		SouthBoundID: "68580342-6b3e-57cb-9ea4-06125594e330",
-		NniPort:      "16777472",
+		NniPort:      []string{"16777472"},
 		Ports:        sync.Map{},
 		PonPortList:  sync.Map{},
 	}
@@ -1115,7 +1115,7 @@
 		Name:         "11c3175b-50f3-4220-9555-93df733ded1d",
 		SerialNum:    "SDX6320031",
 		SouthBoundID: "68580342-6b3e-57cb-9ea4-06125594e330",
-		NniPort:      "16777472",
+		NniPort:      []string{"16777472"},
 		Ports:        sync.Map{},
 		PonPortList:  sync.Map{},
 	}
diff --git a/internal/pkg/application/igmpprofiles.go b/internal/pkg/application/igmpprofiles.go
index 2683a06..f26e33b 100644
--- a/internal/pkg/application/igmpprofiles.go
+++ b/internal/pkg/application/igmpprofiles.go
@@ -318,7 +318,11 @@
 	if err := mvp.WriteToDb(cntx); err != nil {
 		logger.Errorw(ctx, "Mvlan profile write to DB failed", log.Fields{"ProfileName": mvp.Name})
 	}
-	return cntlr.GetController().DelFlows(cntx, device.NniPort, device.Name, flow, false)
+	nniPort, err := GetApplication().GetNniPort(device.Name)
+	if err != nil {
+		logger.Errorw(ctx, "Error getting NNI port", log.Fields{"Error": err})
+	}
+	return cntlr.GetController().DelFlows(cntx, nniPort, device.Name, flow, false)
 }
 
 // FlowRemoveSuccess - Process flow success indication
@@ -404,8 +408,11 @@
 		logger.Warnw(ctx, "Skipping Igmp & Mcast Flow processing: Device Not Found", log.Fields{"Device_SrNo": OLTSerialNum, "Mvlan": mvp.Mvlan})
 		return
 	}
-
-	p := d.GetPort(d.NniPort)
+	nniPort, err := GetApplication().GetNniPort(d.Name)
+	if err != nil {
+		logger.Errorw(ctx, "Error getting NNI port", log.Fields{"Error": err})
+	}
+	p := d.GetPort(nniPort)
 
 	if p != nil && p.State == PortStateUp {
 		logger.Infow(ctx, "NNI Port Status is: UP & Vlan Enabled", log.Fields{"Device": d, "port": p})
@@ -432,7 +439,11 @@
 	defer mvp.mvpLock.RUnlock()
 
 	if d, _ := GetApplication().GetDeviceBySerialNo(oltSerialNum); d != nil {
-		p := d.GetPort(d.NniPort)
+		nniPort, err := GetApplication().GetNniPort(d.Name)
+		if err != nil {
+			logger.Errorw(ctx, "Error getting NNI port", log.Fields{"Error": err})
+		}
+		p := d.GetPort(nniPort)
 		if p != nil {
 			logger.Infow(ctx, "NNI Port Status is: UP", log.Fields{"Device": d, "port": p})
 
@@ -482,7 +493,11 @@
 	if !ok || !flowAlreadyApplied {
 		flows, err := mvp.BuildIgmpDSFlows(device)
 		if err == nil {
-			err = cntlr.GetController().AddFlows(cntx, d.NniPort, device, flows)
+			nniPort, err1 := va.GetNniPort(device)
+			if err1 != nil {
+				logger.Errorw(ctx, "Error getting NNI port", log.Fields{"Error": err1})
+			}
+			err = cntlr.GetController().AddFlows(cntx, nniPort, device, flows)
 			if err != nil {
 				logger.Warnw(ctx, "Configuring IGMP Flow for device failed ", log.Fields{"Device": device, "err": err})
 				return err
@@ -1024,7 +1039,7 @@
 				vp.ChannelPerSubAlarmRaised = false
 			} else if mvp.MaxActiveChannels < vp.ActiveChannels && !vp.ChannelPerSubAlarmRaised {
 				/* When the max active channel count is reduced via update, we raise an alarm.
-				   But the previous excess channels still exist until a leave or expiry */
+				But the previous excess channels still exist until a leave or expiry */
 				serviceName := GetMcastServiceForSubAlarm(vp, mvp)
 				logger.Debugw(ctx, "Raising-SendActiveChannelPerSubscriberAlarm-due-to-update", log.Fields{"ActiveChannels": vp.ActiveChannels, "ServiceName": serviceName})
 				vp.ChannelPerSubAlarmRaised = true
diff --git a/internal/pkg/application/igmpprofiles_test.go b/internal/pkg/application/igmpprofiles_test.go
index e3b7328..411426f 100644
--- a/internal/pkg/application/igmpprofiles_test.go
+++ b/internal/pkg/application/igmpprofiles_test.go
@@ -473,7 +473,7 @@
 		Name:      "SDX6320031",
 		SerialNum: "SDX6320031",
 		Ports:     sync.Map{},
-		NniPort:   "16777472",
+		NniPort:   []string{"16777472"},
 	}
 	voltPort := &VoltPort{
 		Name:                     "16777472",
@@ -527,7 +527,7 @@
 		Name:            "SDX6320031",
 		SerialNum:       "SDX6320031",
 		Ports:           sync.Map{},
-		NniPort:         "16777472",
+		NniPort:         []string{"16777472"},
 		FlowDelEventMap: util.NewConcurrentMap(),
 	}
 	va.DevicesDisc.Store("SDX6320031", d)
@@ -580,7 +580,7 @@
 		Name:            "SDX6320031",
 		SerialNum:       "SDX6320031",
 		Ports:           sync.Map{},
-		NniPort:         "16777472",
+		NniPort:         []string{"16777472"},
 		FlowDelEventMap: util.NewConcurrentMap(),
 	}
 	va.DevicesDisc.Store("SDX6320031", d)
diff --git a/internal/pkg/application/pppoeia_test.go b/internal/pkg/application/pppoeia_test.go
index 0b8ed04..5c9aeac 100644
--- a/internal/pkg/application/pppoeia_test.go
+++ b/internal/pkg/application/pppoeia_test.go
@@ -199,7 +199,7 @@
 				va.DevicesDisc.Store(test_device, voltDevice)
 				pkt.EXPECT().Layers().Return(LayerTypeDot2Q).Times(3)
 				voltPortVnet1[0].SVlan = 0
-				voltDevice.NniPort = "1"
+				voltDevice.NniPort = []string{"1"}
 				va.VnetsByPort.Store("test_port", voltPortVnet1)
 				voltPortVnet1[0].PppoeIa = true
 				voltPortVnet1[0].AllowTransparent = true
diff --git a/internal/pkg/application/service.go b/internal/pkg/application/service.go
index ae6f313..9a771f9 100644
--- a/internal/pkg/application/service.go
+++ b/internal/pkg/application/service.go
@@ -87,6 +87,7 @@
 	Name                       string
 	CircuitID                  string
 	Port                       string
+	NniPort                    string
 	UsMeterProfile             string
 	DsMeterProfile             string
 	AggDsMeterProfile          string
@@ -574,12 +575,27 @@
 	flow := &of.VoltFlow{}
 	flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
 
-	// Get the out and in ports for the flows
 	device, err := GetApplication().GetDeviceFromPort(vs.Port)
 	if err != nil {
-		return nil, fmt.Errorf("Error Getting Device for Service %s and Port %s  : %w", vs.Name, vs.Port, err)
+		return nil, fmt.Errorf("error getting device for service %s and port %s  : %w", vs.Name, vs.Port, err)
 	}
-	inport, _ := GetApplication().GetPortID(device.NniPort)
+	// inport will be obtained from nniPort of service else we'll use the default nni port
+	var inport uint32
+	// Get the out and in ports for the flows
+	if vs.NniPort != "" {
+		if nniPortID := device.GetPortIDFromPortName(vs.NniPort); nniPortID != 0 {
+			inport = nniPortID
+		} else {
+			return nil, fmt.Errorf("error getting portID for NNI port %s of Service %s", vs.NniPort, vs.Name)
+		}
+	} else {
+		nniPort, err1 := GetApplication().GetNniPort(device.Name)
+		if err != nil {
+			logger.Errorw(ctx, "Error getting NNI port", log.Fields{"Error": err1})
+			return nil, err1
+		}
+		inport, _ = GetApplication().GetPortID(nniPort)
+	}
 	outport, _ := GetApplication().GetPortID(vs.Port)
 	// PortName and PortID to be used for validation of port before flow pushing
 	flow.PortID = outport
@@ -757,12 +773,27 @@
 	flow := &of.VoltFlow{}
 	flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
 
-	// Get the out and in ports for the flows
 	device, err := GetApplication().GetDeviceFromPort(vs.Port)
 	if err != nil {
-		return nil, errorCodes.ErrDeviceNotFound
+		return nil, fmt.Errorf("error getting device for service %s and port %s  : %w", vs.Name, vs.Port, err)
 	}
-	outport, _ := GetApplication().GetPortID(device.NniPort)
+	// outport will be obtained from nniPort of service else we'll use the default nni port
+	var outport uint32
+	// Get the out and in ports for the flows
+	if vs.NniPort != "" {
+		if nniPortID := device.GetPortIDFromPortName(vs.NniPort); nniPortID != 0 {
+			outport = nniPortID
+		} else {
+			return nil, fmt.Errorf("error getting portID for NNI port %s of Service %s : %w", vs.NniPort, vs.Name, err)
+		}
+	} else {
+		nniPort, err := GetApplication().GetNniPort(device.Name)
+		if err != nil {
+			logger.Errorw(ctx, "Error getting NNI port", log.Fields{"Error": err})
+			return nil, err
+		}
+		outport, _ = GetApplication().GetPortID(nniPort)
+	}
 	inport, _ := GetApplication().GetPortID(vs.Port)
 	// PortName and PortID to be used for validation of port before flow pushing
 	flow.PortID = inport
@@ -2192,7 +2223,11 @@
 				if p.State == PortStateUp {
 					if vpv := va.GetVnetByPort(vs.Port, vs.SVlan, vs.CVlan, vs.UniVlan); vpv != nil {
 						// PortUp call initiates flow addition
-						vpv.PortUpInd(cntx, device, portNo)
+						// The flow generation and pushing the flow can be done in a go routine,
+						// VGC once service is activated remembers and pushes the flows again
+						// if there was a restart in VGC during the execution of the go routine.
+						// Making it as a go routine will not impact anything
+						go vpv.PortUpInd(cntx, device, portNo, vs.NniPort)
 					} else {
 						logger.Warnw(ctx, "VPV does not exists!!!", log.Fields{"Device": deviceID, "port": portNo, "SvcName": vs.Name})
 					}
diff --git a/internal/pkg/application/service_test.go b/internal/pkg/application/service_test.go
index 746666a..76b0ec9 100644
--- a/internal/pkg/application/service_test.go
+++ b/internal/pkg/application/service_test.go
@@ -48,7 +48,7 @@
 	FlowDelEventMap:              util.NewConcurrentMap(),
 	SerialNum:                    "test_serial_number",
 	ConfiguredVlanForDeviceFlows: util.NewConcurrentMap(),
-	NniPort:                      "16777216",
+	NniPort:                      []string{"16777216"},
 }
 
 var voltMeter = &VoltMeter{
@@ -327,6 +327,7 @@
 			dbintf.EXPECT().PutService(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
 			ga.PortsDisc.Store(test_device, voltPort)
 			ga.DevicesDisc.Store(test_device, voltDevice)
+			ga.DevicesConfig.Store("test_serial_number", &DeviceConfig{UplinkPort: "16777216"})
 			vs.SvcUpInd(tt.args.cntx)
 		})
 	}
diff --git a/internal/pkg/application/vnets.go b/internal/pkg/application/vnets.go
index 80e8212..ef60ce3 100644
--- a/internal/pkg/application/vnets.go
+++ b/internal/pkg/application/vnets.go
@@ -749,7 +749,7 @@
 	// vpv.DsFlowsApplied = false
 	// vpv.UsFlowsApplied = false
 	vpv.VpvLock.Lock()
-	vpv.PortUpInd(cntx, d, vpv.Port)
+	vpv.PortUpInd(cntx, d, vpv.Port, "")
 	vpv.VpvLock.Unlock()
 }
 
@@ -758,7 +758,7 @@
 // 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(cntx context.Context, device *VoltDevice, port string) {
+func (vpv *VoltPortVnet) PortUpInd(cntx context.Context, device *VoltDevice, port string, nniPort string) {
 	logger.Infow(ctx, "Port UP Ind, pushing flows for the port", log.Fields{"Device": device, "Port": port, "VnetDhcp": vpv.DhcpRelay, "McastService": vpv.McastService})
 	if vpv.DeleteInProgress {
 		logger.Warnw(ctx, "Ignoring VPV Port UP Ind, VPV deletion In-Progress", log.Fields{"Device": device, "Port": port, "Vnet": vpv.VnetName})
@@ -766,20 +766,17 @@
 	}
 	vpv.setDevice(device.Name)
 
-	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 nniPort := device.GetPort(nni); nniPort != nil {
-		//If NNI port is not mached to nb nni port dont send flows
-		devConfig := GetApplication().GetDeviceConfig(device.SerialNum)
-		if devConfig != nil {
-			if devConfig.UplinkPort != strconv.Itoa(int(nniPort.ID)) {
-				logger.Warnw(ctx, "NNI port not configured from NB, not pushing flows", log.Fields{"NB NNI Port": devConfig.UplinkPort, "SB NNI port": nniPort.ID})
-				return
-			}
+	if nniPort != "" {
+		err := vpv.ValidateNniPort(device, nniPort)
+		if err != nil {
+			logger.Warnw(ctx, "Ignoring Vnet Port UP indication: NNI is not configured", log.Fields{"Port": vpv.Port, "Device": device.Name, "NNI": nniPort, "Error": err})
+			return
+		}
+	} else {
+		nni, err := GetApplication().GetNniPort(device.Name)
+		if nni == "" {
+			logger.Warnw(ctx, "Ignoring Vnet Port UP indication: Default NNI is unavailable", log.Fields{"Port": vpv.Port, "Device": device.Name, "Error": err})
+			return
 		}
 	}
 
@@ -1012,6 +1009,40 @@
 	return dsPbit
 }
 
+func (vpv *VoltPortVnet) ValidateNniPort(device *VoltDevice, nniPortName string) error {
+	devConfig := GetApplication().GetDeviceConfig(device.SerialNum)
+	if devConfig == nil {
+		return fmt.Errorf("device config not found for serial number %s", device.SerialNum)
+	}
+
+	var nniPort string
+	var nniPortID uint32
+	if nniPortID = device.GetPortIDFromPortName(nniPortName); nniPortID == 0 {
+		logger.Errorw(ctx, "Port Not Found", log.Fields{"NNI Port": nniPortName})
+		return errors.New("port not found for service")
+	}
+	if !device.IsPortNni(nniPortName) {
+		logger.Errorw(ctx, "Port Not Found in device", log.Fields{"NNI Port": nniPortName})
+		return fmt.Errorf("port not found in device")
+	}
+
+	nniPort = strconv.Itoa(int(nniPortID))
+	if len(devConfig.NniPorts) > 0 {
+		for _, port := range devConfig.NniPorts {
+			if port == nniPort {
+				logger.Debugw(ctx, "NNI port is configured from NB", log.Fields{"NB NniPorts": devConfig.NniPorts, "NniPort": nniPortName})
+				return nil // Match found
+			}
+		}
+	} else {
+		if devConfig.UplinkPort == nniPort {
+			logger.Debugw(ctx, "NNI port is configured as default from NB", log.Fields{"NB NNI Port": devConfig.UplinkPort, "SB NNI Ports": device.NniPort})
+			return nil // Match found
+		}
+	}
+	return fmt.Errorf("nni port mismatch: NB NNI Port: %s, SB NNI Ports: %v", devConfig.UplinkPort, device.NniPort)
+}
+
 // AddSvc adds a service on the VNET on a port. The addition is
 // triggered when NB requests for service addition
 func (vpv *VoltPortVnet) AddSvc(cntx context.Context, svc *VoltService) {
@@ -1084,12 +1115,18 @@
 		return
 	}
 
-	// If NNI port is not mached to nb nni port
-	devConfig := GetApplication().GetDeviceConfig(voltDevice.SerialNum)
-
-	if devConfig.UplinkPort != voltDevice.NniPort {
-		logger.Errorw(ctx, "NNI port mismatch", log.Fields{"NB NNI Port": devConfig.UplinkPort, "SB NNI port": voltDevice.NniPort})
-		return
+	if svc.NniPort != "" {
+		err := vpv.ValidateNniPort(voltDevice, svc.NniPort)
+		if err != nil {
+			logger.Warnw(ctx, "Not pushing service flows: NNI is not configured", log.Fields{"Port": vpv.Port, "Device": voltDevice.Name, "NNI": svc.NniPort, "Error": err})
+			return
+		}
+	} else {
+		nni, err := GetApplication().GetNniPort(voltDevice.Name)
+		if nni == "" {
+			logger.Warnw(ctx, "Not pushing service flows: Default NNI is unavailable", log.Fields{"Port": vpv.Port, "Device": voltDevice.Name, "Error": err})
+			return
+		}
 	}
 	// Push Service Flows if DHCP relay is not configured
 	// or already DHCP flows are configured for the VPV
@@ -1332,6 +1369,90 @@
 	vpv.RangeOnServices(cntx, ClearServiceCounters, false)
 }
 
+// BuildDefaultDhcpTrapFlow to build the downstream dhcp flows
+func (va *VoltApplication) BuildDefaultDhcpTrapFlow(ctx context.Context, device *VoltDevice, port string) (*of.VoltFlow, error) {
+	logger.Infow(ctx, "Building NNI DS DHCP flow", log.Fields{"Port": port, "device": device.Name})
+	flow := &of.VoltFlow{}
+	flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
+	subFlow := of.NewVoltSubFlow()
+	subFlow.SetTableID(0)
+
+	subFlow.SetUdpv4Match()
+	subFlow.SrcPort = 67
+	subFlow.DstPort = 68
+
+	nniport, err := GetApplication().GetPortID(port)
+	if err != nil {
+		return nil, fmt.Errorf("failed to fetch port id %d for nni : %w", nniport, err)
+	}
+	subFlow.SetInPort(nniport)
+	// PortName and PortID to be used for validation of port before flow pushing
+	flow.PortID = nniport
+	flow.PortName = port
+	allowTransparent := 0
+
+	ontEtherTypeClass := 0
+	vlanControl := OLTSVlan
+	uniVlan := of.VlanNone
+	cVlan := of.VlanNone
+
+	metadata := uint64(allowTransparent)<<56 | uint64(ontEtherTypeClass)<<36 | uint64(vlanControl)<<32 | uint64(uniVlan)<<16 | uint64(cVlan)
+	subFlow.SetTableMetadata(metadata)
+	subFlow.Priority = of.DhcpFlowPriority
+
+	subFlow.SetReportToController()
+	// | 12-bit cvlan | 4 bits empty | <32-bits uniport>| 16-bits dhcp mask or flow mask |
+	subFlow.Cookie = uint64(cVlan)<<52 | uint64(nniport)<<16 | of.DhcpArpFlowMask | of.DsFlowMask
+
+	flow.SubFlows[subFlow.Cookie] = subFlow
+	logger.Infow(ctx, "Built NNI DS DHCP flow ", log.Fields{"cookie": subFlow.Cookie, "Flow": flow})
+
+	return flow, nil
+}
+
+// AddDefaultDhcpTrapFlow function pushes the default DHCP flows to the VOLTHA via the controller
+func (va *VoltApplication) AddDefaultDhcpTrapFlow(cntx context.Context, vd *VoltDevice, p *VoltPort) error {
+	if vd.GlobalDhcpFlowAdded {
+		logger.Info(ctx, "Global Dhcp flow already exists")
+		return nil
+	}
+
+	flows, err := va.BuildDefaultDhcpTrapFlow(cntx, vd, p.Name)
+	if err == nil {
+		if err1 := va.PushTrapFlows(cntx, vd, p.Name, flows); err1 != nil {
+			logger.Errorw(cntx, "Failure pushing nni trap flows", log.Fields{"device": vd.Name, "port": p.Name})
+			return fmt.Errorf("failed to push trap flow for nni : %s, error : %w", p.Name, err1)
+		}
+	} else {
+		logger.Errorw(cntx, "Failure building nni trap flows", log.Fields{"device": vd.Name, "port": p.Name})
+		return fmt.Errorf("failed to build trap flow for nni : %s, error : %w", p.Name, err)
+	}
+	p.NniDhcpTrapFlowAdded = true
+	if GetApplication().GetVendorID() != Radisys && va.IsDhcpTrapFlowAdded(cntx, vd) {
+		logger.Debugw(ctx, "Global DHCP trap flow set", log.Fields{"device": vd.Name})
+		vd.GlobalDhcpFlowAdded = true
+	}
+	return nil
+}
+
+func (va *VoltApplication) IsDhcpTrapFlowAdded(ctx context.Context, vd *VoltDevice) bool {
+	if len(vd.NniPort) == 0 {
+		return false
+	}
+	flag := true
+	for _, nniPort := range vd.NniPort {
+		if p := vd.GetPort(nniPort); p != nil {
+			if !p.NniDhcpTrapFlowAdded {
+				flag = false
+			}
+		} else {
+			flag = false
+		}
+	}
+
+	return flag
+}
+
 // AddUsDhcpFlows pushes the DHCP flows to the VOLTHA via the controller
 func (vpv *VoltPortVnet) AddUsDhcpFlows(cntx context.Context) error {
 	var vd *VoltDevice
@@ -1380,30 +1501,49 @@
 		err := errorCodes.ErrDeviceNotFound
 		return fmt.Errorf("ds dhcp flow push failed - device not found for Port %s, Svlan %d, Cvlan %d, UniVlan %d. Device %s : %w", vpv.Port, vpv.SVlan, vpv.CVlan, vpv.UniVlan, device, err)
 	}
+
 	if vd.GlobalDhcpFlowAdded {
 		logger.Info(ctx, "Global Dhcp flow already exists")
 		return nil
 	}
 
-	flows, err := vpv.BuildDsDhcpFlows()
-	if err == nil {
-		if err1 := vpv.PushFlows(cntx, vd, flows); err1 != nil {
-			// push ind here and procced
-			statusCode, statusMessage := errorCodes.GetErrorInfo(err1)
-			vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
-		}
+	nniPorts := make([]string, 0)
+	vpv.services.Range(func(key, value interface{}) bool {
+		svc := value.(*VoltService)
+		logger.Infow(ctx, "Found service on the vpv", log.Fields{"Name": svc.Name, "NNIPort": svc.NniPort})
+		nniPorts = append(nniPorts, svc.NniPort)
+		return true
+	})
+
+	if len(nniPorts) == 0 {
+		vpv.BuildAndPushDSDhcpFlows(cntx, vd, "")
 	} else {
-		logger.Errorw(ctx, "DS DHCP Flow Add Failed", log.Fields{"Reason": err.Error()})
-		// send ind here and proceed
-		statusCode, statusMessage := errorCodes.GetErrorInfo(err)
-		vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+		for _, nniPort := range nniPorts {
+			vpv.BuildAndPushDSDhcpFlows(cntx, vd, nniPort)
+		}
 	}
+
 	if GetApplication().GetVendorID() != Radisys {
 		vd.GlobalDhcpFlowAdded = true
 	}
 	return nil
 }
 
+func (vpv *VoltPortVnet) BuildAndPushDSDhcpFlows(cntx context.Context, device *VoltDevice, nniPort string) {
+	var err error
+	var flows *of.VoltFlow
+	flows, err = vpv.BuildDsDhcpFlows(nniPort)
+	if err == nil {
+		if err1 := vpv.PushFlows(cntx, device, flows); err1 != nil {
+			statusCode, statusMessage := errorCodes.GetErrorInfo(err1)
+			vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+		}
+	} else {
+		statusCode, statusMessage := errorCodes.GetErrorInfo(err)
+		vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
+	}
+}
+
 // DelDhcpFlows deletes both US & DS DHCP flows applied for this Vnet instantiated on the port
 func (vpv *VoltPortVnet) DelDhcpFlows(cntx context.Context) {
 	logger.Info(ctx, "Received Delete DHCP Flows")
@@ -1471,11 +1611,42 @@
 
 func (vpv *VoltPortVnet) delDsDhcp4Flows(cntx context.Context, device *VoltDevice) error {
 	logger.Debugw(ctx, "Received DS Delete DHCP4 Flows", log.Fields{"DeviceName": device.Name})
-	flows, err := vpv.BuildDsDhcpFlows()
-	if err == nil {
-		return vpv.RemoveFlows(cntx, device, flows)
+	nniPorts := make([]string, 0)
+	vpv.services.Range(func(key, value interface{}) bool {
+		svc := value.(*VoltService)
+		logger.Infow(ctx, "Found service on the vpv", log.Fields{"Name": svc.Name, "NNIPort": svc.NniPort})
+		nniPorts = append(nniPorts, svc.NniPort)
+		return true
+	})
+
+	if len(nniPorts) == 0 {
+		if err := vpv.BuildAndRemoveDSDhcpFlows(cntx, device, ""); err != nil {
+			return err
+		}
+	} else {
+		for _, nniPort := range nniPorts {
+			if err := vpv.BuildAndRemoveDSDhcpFlows(cntx, device, nniPort); err != nil {
+				return err
+			}
+		}
 	}
-	return fmt.Errorf("DS DHCP Flow Delete Failed : %w", err)
+	return nil
+}
+
+func (vpv *VoltPortVnet) BuildAndRemoveDSDhcpFlows(cntx context.Context, device *VoltDevice, nniPort string) error {
+	var err error
+	var flows *of.VoltFlow
+	flows, err = vpv.BuildDsDhcpFlows(nniPort)
+	if err == nil {
+		if err1 := vpv.RemoveFlows(cntx, device, flows); err1 != nil {
+			logger.Errorw(ctx, "DS DHCP Flow Remove Failed", log.Fields{"Reason": err.Error()})
+			return fmt.Errorf("ds dhcp flow remove failed - Port %s, NNIPort %s, Svlan %d, Cvlan %d, UniVlan %d. Device %s : %w", vpv.Port, nniPort, vpv.SVlan, vpv.CVlan, vpv.UniVlan, vpv.Device, err)
+		}
+	} else {
+		logger.Errorw(ctx, "DS DHCP Flow Build Failed", log.Fields{"Reason": err.Error()})
+		return fmt.Errorf("ds dhcp flow build failed - Port %s, NNIPort %s, Svlan %d, Cvlan %d, UniVlan %d. Device %s : %w", vpv.Port, nniPort, vpv.SVlan, vpv.CVlan, vpv.UniVlan, vpv.Device, err)
+	}
+	return nil
 }
 
 /*
@@ -1752,8 +1923,8 @@
 }
 
 // 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})
+func (vpv *VoltPortVnet) BuildDsDhcpFlows(nniPort string) (*of.VoltFlow, error) {
+	logger.Infow(ctx, "Building DS DHCP flow", log.Fields{"Port": vpv.Port, "ML": vpv.MacLearning, "Mac": vpv.MacAddr, "NNIPort": nniPort})
 	flow := &of.VoltFlow{}
 	flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
 	subFlow := of.NewVoltSubFlow()
@@ -1766,15 +1937,28 @@
 	subFlow.SrcPort = 67
 	subFlow.DstPort = 68
 	uniport, _ := GetApplication().GetPortID(vpv.Port)
-	nni, err := GetApplication().GetNniPort(vpv.Device)
+
+	device, err := GetApplication().GetDeviceFromPort(vpv.Port)
 	if err != nil {
-		return nil, fmt.Errorf("failed to fetch nni port %s from vpv : %w", nni, err)
+		return nil, fmt.Errorf("error getting device for vpv %s and port %s  : %w", vpv.VnetName, vpv.Port, err)
 	}
-	nniport, err := GetApplication().GetPortID(nni)
-	if err != nil {
-		return nil, fmt.Errorf("failed to fetch port id %d for nni : %w", nniport, err)
+	var inport uint32
+	if nniPort != "" {
+		if nniPortID := device.GetPortIDFromPortName(nniPort); nniPortID != 0 {
+			inport = nniPortID
+		} else {
+			return nil, fmt.Errorf("error getting portID for NNI port %s : %w", nniPort, err)
+		}
+	} else {
+		nniPort, err := GetApplication().GetNniPort(device.Name)
+		if err != nil {
+			logger.Errorw(ctx, "Error getting NNI port", log.Fields{"Error": err})
+			return nil, err
+		}
+		inport, _ = GetApplication().GetPortID(nniPort)
 	}
-	subFlow.SetInPort(nniport)
+
+	subFlow.SetInPort(inport)
 	// PortName and PortID to be used for validation of port before flow pushing
 	flow.PortID = uniport
 	flow.PortName = vpv.Port
@@ -2287,7 +2471,7 @@
 		if p != nil {
 			logger.Debugw(ctx, "Checking UNI port state", log.Fields{"State": p.State})
 			if d.State == controller.DeviceStateUP && p.State == PortStateUp {
-				vpv.PortUpInd(cntx, d, port)
+				vpv.PortUpInd(cntx, d, port, vs.NniPort)
 			}
 		}
 	}
@@ -2384,7 +2568,7 @@
 
 	// 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 {
+	if d.IsPortNni(port) {
 		return nil
 	}
 
@@ -2449,7 +2633,7 @@
 
 	// 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 {
+	if d.IsPortNni(port) {
 		return nil, nil
 	}
 
@@ -2530,15 +2714,20 @@
 			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 {
+		nniPort, err := GetApplication().GetNniPort(device.Name)
+		if err != nil {
+			logger.Errorw(ctx, "Error getting NNI port", log.Fields{"Error": err})
+			return true
+		}
+		if portID, err := va.GetPortID(nniPort); err == nil {
+			if state, _ := cntlr.GetController().GetPortState(device.Name, 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(cntx, device.NniPort, device.Name, flow)
+			err = cntlr.GetController().AddFlows(cntx, 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
@@ -2547,7 +2736,7 @@
 
 			// Pushing ARP Flow
 			flow = BuildDSArpFlow(portID, vnet)
-			err = cntlr.GetController().AddFlows(cntx, device.NniPort, device.Name, flow)
+			err = cntlr.GetController().AddFlows(cntx, 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
@@ -2563,6 +2752,11 @@
 	va.DevicesDisc.Range(pushflow)
 }
 
+func (va *VoltApplication) PushTrapFlows(cntx context.Context, device *VoltDevice, nniPort string, flow *of.VoltFlow) error {
+	logger.Debugw(ctx, "Push NNI DHCP Trap Flows", log.Fields{"DeviceName": device.Name, "Flow port": flow.PortID})
+	return cntlr.GetController().AddFlows(cntx, nniPort, device.Name, flow)
+}
+
 // PushDevFlowForDevice to push icmpv6 flows for device
 func (va *VoltApplication) PushDevFlowForDevice(cntx context.Context, device *VoltDevice) {
 	logger.Debugw(ctx, "PushDevFlowForDevice", log.Fields{"device": device.Name})
@@ -2580,7 +2774,12 @@
 			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)
+		nniPort, err := GetApplication().GetNniPort(device.Name)
+		if err != nil {
+			logger.Errorw(ctx, "Error getting NNI port", log.Fields{"Error": err})
+			return true
+		}
+		nniPortID, err := va.GetPortID(nniPort)
 		if err != nil {
 			logger.Errorw(ctx, "Push ICMPv6 Failed - Failed to get NNI Port Id", log.Fields{"Port": device.NniPort, "Reason": err.Error})
 		}
@@ -2589,7 +2788,7 @@
 			return true
 		}
 		flow := BuildICMPv6Flow(nniPortID, vnet)
-		err = cntlr.GetController().AddFlows(cntx, device.NniPort, device.Name, flow)
+		err = cntlr.GetController().AddFlows(cntx, 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
@@ -2597,7 +2796,7 @@
 		logger.Infow(ctx, "ICMP Flow Added to Queue", log.Fields{"flow": flow})
 
 		flow = BuildDSArpFlow(nniPortID, vnet)
-		err = cntlr.GetController().AddFlows(cntx, device.NniPort, device.Name, flow)
+		err = cntlr.GetController().AddFlows(cntx, 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
@@ -2627,8 +2826,13 @@
 				return true
 			}
 		}
-		if portID, err := va.GetPortID(device.NniPort); err == nil {
-			if state, _ := cntlr.GetController().GetPortState(device.Name, device.NniPort); state != cntlr.PortStateUp {
+		nniPort, err := GetApplication().GetNniPort(device.Name)
+		if err != nil {
+			logger.Errorw(ctx, "Error getting NNI port", log.Fields{"Error": err})
+			return true
+		}
+		if portID, err := va.GetPortID(nniPort); err == nil {
+			if state, _ := cntlr.GetController().GetPortState(device.Name, nniPort); state != cntlr.PortStateUp {
 				logger.Warnw(ctx, "Skipping ICMPv6 Flow Deletion - Port Down", log.Fields{"Device": device})
 				return true
 			}
@@ -2676,7 +2880,12 @@
 			logger.Warnw(ctx, "ICMPv6 Flow map entry not found for Vnet", log.Fields{"Vnet": vnet.VnetConfig})
 			return true
 		}
-		nniPortID, err := va.GetPortID(device.NniPort)
+		nniPort, err := GetApplication().GetNniPort(device.Name)
+		if err != nil {
+			logger.Errorw(ctx, "Error getting NNI port", log.Fields{"Error": err})
+			return true
+		}
+		nniPortID, err := va.GetPortID(nniPort)
 		if err != nil {
 			logger.Errorw(ctx, "Delete ICMPv6 Failed - Failed to get NNI Port Id", log.Fields{"Port": device.NniPort, "Reason": err.Error})
 		}
@@ -2738,8 +2947,13 @@
 			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 {
+		nniPort, err := GetApplication().GetNniPort(device.Name)
+		if err != nil {
+			logger.Errorw(ctx, "Error getting NNI port", log.Fields{"Error": err})
+			return true
+		}
+		if portID, err := va.GetPortID(nniPort); err == nil {
+			if state, _ := cntlr.GetController().GetPortState(device.Name, nniPort); state != cntlr.PortStateUp {
 				logger.Warnw(ctx, "Skipping ICMPv6 Flow Deletion - Port Down", log.Fields{"Device": device})
 				return false
 			}
@@ -3023,7 +3237,12 @@
 		vv.PendingDeleteFlow[device.Name] = flowMap
 	}
 	vv.WriteToDb(cntx)
-	return cntlr.GetController().DelFlows(cntx, device.NniPort, device.Name, flow, false)
+	nniPort, err := GetApplication().GetNniPort(device.Name)
+	if err != nil {
+		logger.Errorw(ctx, "Error getting NNI port", log.Fields{"Error": err})
+		return err
+	}
+	return cntlr.GetController().DelFlows(cntx, nniPort, device.Name, flow, false)
 }
 
 // CheckAndDeleteVnet - remove Vnet from DB is there are no pending flows to be removed
@@ -3116,7 +3335,7 @@
 
 	// 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 {
+	if d.IsPortNni(port) {
 		return nil
 	}
 
@@ -3246,17 +3465,19 @@
 	})
 }
 
-func (vpv *VoltPortVnet) IsServiceActivated(cntx context.Context) bool {
+func (vpv *VoltPortVnet) IsServiceActivated(cntx context.Context) (bool, string) {
 	logger.Debugw(ctx, "Is Service Activated", log.Fields{"Name": vpv.Port})
 	isActivated := false
+	nniPort := ""
 	vpv.services.Range(func(key, value interface{}) bool {
 		svc := value.(*VoltService)
 		if svc.IsActivated {
 			logger.Infow(ctx, "Found activated service on the vpv", log.Fields{"Name": svc.Name})
 			isActivated = true
+			nniPort = svc.NniPort
 			return false //to exit loop
 		}
 		return true
 	})
-	return isActivated
+	return isActivated, nniPort
 }
diff --git a/internal/pkg/application/vnets_test.go b/internal/pkg/application/vnets_test.go
index d0f56ae..7016667 100644
--- a/internal/pkg/application/vnets_test.go
+++ b/internal/pkg/application/vnets_test.go
@@ -85,7 +85,7 @@
 				},
 			}
 			vpv.services.Store(test_device, voltServ)
-			if got := vpv.IsServiceActivated(tt.args.cntx); got != tt.want {
+			if got, _ := vpv.IsServiceActivated(tt.args.cntx); got != tt.want {
 				t.Errorf("VoltPortVnet.IsServiceActivated() = %v, want %v", got, tt.want)
 			}
 		})
@@ -226,7 +226,7 @@
 				}
 			case "port == d.NniPort":
 				va.DevicesDisc.Store(test_device, voltDevice)
-				voltDevice.NniPort = "test_port"
+				voltDevice.NniPort = []string{"test_port"}
 				if got := va.GetMatchingMcastService(tt.args.port, tt.args.device, tt.args.cvlan); !reflect.DeepEqual(got, tt.want) {
 					t.Errorf("VoltApplication.GetMatchingMcastService() = %v, want %v", got, tt.want)
 				}
@@ -621,7 +621,7 @@
 		SerialNum:                    "SDX6320031",
 		NniDhcpTrapVid:               123,
 		State:                        cntlr.DeviceStateUP,
-		NniPort:                      "16777472",
+		NniPort:                      []string{"16777472"},
 		Ports:                        sync.Map{},
 		FlowDelEventMap:              util.NewConcurrentMap(),
 		ConfiguredVlanForDeviceFlows: util.NewConcurrentMap(),
@@ -780,7 +780,7 @@
 		SerialNum:                    "SDX6320031",
 		NniDhcpTrapVid:               123,
 		State:                        cntlr.DeviceStateUP,
-		NniPort:                      "16777472",
+		NniPort:                      []string{"16777472"},
 		FlowDelEventMap:              util.NewConcurrentMap(),
 		ConfiguredVlanForDeviceFlows: util.NewConcurrentMap(),
 		icmpv6GroupAdded:             true,
@@ -906,7 +906,7 @@
 		SerialNum:                    "SDX6320031",
 		NniDhcpTrapVid:               123,
 		State:                        cntlr.DeviceStateUP,
-		NniPort:                      "16777472",
+		NniPort:                      []string{"16777472"},
 		FlowDelEventMap:              util.NewConcurrentMap(),
 		ConfiguredVlanForDeviceFlows: util.NewConcurrentMap(),
 		icmpv6GroupAdded:             true,
@@ -1013,7 +1013,7 @@
 				device: &VoltDevice{
 					Name:                         test_device,
 					ConfiguredVlanForDeviceFlows: util.NewConcurrentMap(),
-					NniPort:                      "test_nni_port",
+					NniPort:                      []string{"test_nni_port"},
 				},
 			},
 		},
@@ -1024,7 +1024,7 @@
 				device: &VoltDevice{
 					Name:                         test_device,
 					ConfiguredVlanForDeviceFlows: util.NewConcurrentMap(),
-					NniPort:                      "test_nni_port",
+					NniPort:                      []string{"test_nni_port"},
 					VlanPortStatus:               sync.Map{},
 				},
 			},
@@ -1771,7 +1771,7 @@
 				vpv.Device = deviceName
 				voltDev.State = cntlr.DeviceStateUP
 				voltDev.GlobalDhcpFlowAdded = false
-				voltDev.NniPort = "16777472"
+				voltDev.NniPort = []string{"16777472"}
 				va.PortsDisc.Store("16777472", voltPort)
 				appMock := mocks.NewMockApp(gomock.NewController(t))
 				cntlr.NewController(ctx, appMock)
@@ -1798,7 +1798,7 @@
 		SerialNum:       "SDX6320031",
 		NniDhcpTrapVid:  123,
 		State:           cntlr.DeviceStateUP,
-		NniPort:         "16777472",
+		NniPort:         []string{"16777472"},
 		FlowAddEventMap: util.NewConcurrentMap(),
 	}
 	va.DevicesDisc.Store("SDX6320031", voltDev)
@@ -1893,7 +1893,7 @@
 		SerialNum:       "SDX6320031",
 		NniDhcpTrapVid:  123,
 		State:           cntlr.DeviceStateUP,
-		NniPort:         "16777472",
+		NniPort:         []string{"16777472"},
 		FlowAddEventMap: util.NewConcurrentMap(),
 	}
 	voltPort := &VoltPort{
@@ -2059,12 +2059,20 @@
 			UniVlan: of.VlanAny,
 		},
 	}
+	deviceConfig := &DeviceConfig{
+		SerialNumber:       "SDX6320031",
+		HardwareIdentifier: "dummy_hardware_identifier",
+		IPAddress:          "10.9.8.7",
+		UplinkPort:         "16777216",
+		NasID:              "nas_id",
+		NniDhcpTrapVid:     123,
+	}
 	voltDev := &VoltDevice{
 		Name:           "49686e2d-618f-4e8e-bca0-442ab850a63a",
 		SerialNum:      "SDX6320031",
 		NniDhcpTrapVid: 123,
 		State:          cntlr.DeviceStateUP,
-		NniPort:        "16777216",
+		NniPort:        []string{"16777472"},
 		Ports:          sync.Map{},
 	}
 	tests := []struct {
@@ -2116,7 +2124,9 @@
 				}
 				assert.NotNil(t, got)
 			case "BuildDsDhcp6Flows":
+				voltDev.NniPort = []string{"16777216"}
 				va.DevicesDisc.Store("SDX6320031", voltDev)
+				va.DevicesConfig.Store("SDX6320031", deviceConfig)
 				got, err := vpv.BuildDsDhcp6Flows()
 				if (err != nil) != tt.wantErr {
 					t.Errorf("VoltPortVnet.BuildDsDhcp6Flows() error = %v, wantErr %v", err, tt.wantErr)
@@ -2140,7 +2150,7 @@
 				}
 				assert.Nil(t, got)
 			case "BuildDsDhcp6Flows_portnotfound":
-				voltDev.NniPort = "abc"
+				voltDev.NniPort = []string{"abc"}
 				got, err := vpv.BuildDsDhcp6Flows()
 				if (err != nil) != tt.wantErr {
 					t.Errorf("VoltPortVnet.BuildDsDhcp6Flows_portnotfound() error = %v, wantErr %v", err, tt.wantErr)
@@ -2447,7 +2457,7 @@
 		SerialNum:       "SDX6320031",
 		NniDhcpTrapVid:  123,
 		State:           cntlr.DeviceStateUP,
-		NniPort:         "16777472",
+		NniPort:         []string{"16777472"},
 		Ports:           sync.Map{},
 		FlowDelEventMap: util.NewConcurrentMap(),
 	}
@@ -2524,7 +2534,7 @@
 		SerialNum:                    "SDX6320031",
 		NniDhcpTrapVid:               123,
 		State:                        cntlr.DeviceStateUP,
-		NniPort:                      "16777472",
+		NniPort:                      []string{"16777472"},
 		Ports:                        sync.Map{},
 		FlowDelEventMap:              util.NewConcurrentMap(),
 		ConfiguredVlanForDeviceFlows: util.NewConcurrentMap(),
diff --git a/internal/pkg/util/utils.go b/internal/pkg/util/utils.go
index 36aab0b..9b22576 100644
--- a/internal/pkg/util/utils.go
+++ b/internal/pkg/util/utils.go
@@ -146,9 +146,22 @@
 }
 
 // GetUniPortFromFlow returns uni port from the flow data
-func GetUniPortFromFlow(nniPort string, flow *of.VoltSubFlow) uint32 {
+func GetUniPortFromFlow(uplinkPort string, nniPorts []string, flow *of.VoltSubFlow) uint32 {
 	var portNo uint32
-	if nniPort == strconv.Itoa(int(flow.Match.InPort)) {
+	var isDSFlow bool
+
+	if len(nniPorts) > 0 {
+		for _, nniPort := range nniPorts {
+			if nniPort == strconv.Itoa(int(flow.Match.InPort)) {
+				isDSFlow = true
+				break
+			}
+		}
+	} else if uplinkPort == strconv.Itoa(int(flow.Match.InPort)) {
+		isDSFlow = true
+	}
+
+	if isDSFlow {
 		if of.IPProtocolUDP == flow.Match.L4Protocol {
 			// For DHCP DS flow, uniport is not part of metadata. Hence retrieve it from cookie
 			portNo = GetUniFromDSDhcpFlow(flow.Cookie)
@@ -158,6 +171,7 @@
 	} else {
 		portNo = flow.Match.InPort
 	}
+
 	return portNo
 }
 
diff --git a/voltha-go-controller/nbi/subscriber.go b/voltha-go-controller/nbi/subscriber.go
index f42f109..31410ae 100644
--- a/voltha-go-controller/nbi/subscriber.go
+++ b/voltha-go-controller/nbi/subscriber.go
@@ -135,14 +135,7 @@
 	var voltAppIntr app.VoltAppInterface
 	voltApp := app.GetApplication()
 	voltAppIntr = voltApp
-	if len(srvInfo.UniTagList) == 0 {
-		logger.Infow(ctx, "Received OLT configuration", log.Fields{"req": srvInfo})
-		err := voltAppIntr.AddDeviceConfig(cntx, srvInfo.ID, srvInfo.HardwareIdentifier, srvInfo.NasID, srvInfo.IPAddress, srvInfo.UplinkPort, srvInfo.NniDhcpTrapVid)
-		if err != nil {
-			logger.Warnw(ctx, "Device config addition failed :", log.Fields{"req": srvInfo, "Reason": err.Error()})
-		}
-		return
-	}
+
 	for _, uniTagInfo := range srvInfo.UniTagList {
 		var vs app.VoltServiceCfg
 
@@ -154,6 +147,7 @@
 		vs.Name = svcname + strconv.Itoa(uniTagInfo.TechnologyProfileID)
 
 		vs.Port = srvInfo.NasPortID
+		vs.NniPort = srvInfo.UplinkPort
 		vs.SVlan = of.VlanType(uniTagInfo.PonSTag)
 		vs.CVlan = of.VlanType(uniTagInfo.PonCTag)
 		vs.UniVlan = of.VlanType(uniTagInfo.UniTagMatch)