Updated Adapter to support to handle DHCP trap on NNI and packet-in/out and Bug Fixing.
Tested EAPOL/DHCP/HSIA functionality E2E with EdgeCore OLT and TWSH ONU KIT.

patch: PON port is derived from platform and sent to core and bug fixes

Retested EAPOL/DHCP/HSIA use case end to end with EdgeCore OLT and TWSH ONU KIT

Change-Id: I99df82fd7a1385c10878f6fe09ce0d30c48d8e99
diff --git a/adaptercore/device_handler.go b/adaptercore/device_handler.go
index 3bb34db..866f815 100644
--- a/adaptercore/device_handler.go
+++ b/adaptercore/device_handler.go
@@ -27,6 +27,7 @@
 
 	"github.com/gogo/protobuf/proto"
 	"github.com/golang/protobuf/ptypes"
+	"github.com/mdlayher/ethernet"
 	com "github.com/opencord/voltha-go/adapters/common"
 	"github.com/opencord/voltha-go/common/log"
 	rsrcMgr "github.com/opencord/voltha-openolt-adapter/adaptercore/resourcemanager"
@@ -128,12 +129,7 @@
 	} else {
 		operStatus = voltha.OperStatus_DISCOVERED
 	}
-	// portNum := IntfIdToPortNo(intfId,portType)
-	portNum := intfId
-	if portType == voltha.Port_ETHERNET_NNI {
-		portNum = IntfIdToPortNo(intfId, portType)
-	}
-	// portNum := IntfIdToPortNo(intfId,portType)
+	portNum := IntfIdToPortNo(intfId, portType)
 	label := GetportLabel(portNum, portType)
 	if len(label) == 0 {
 		log.Errorw("Invalid-port-label", log.Fields{"portNum": portNum, "portType": portType})
@@ -251,6 +247,7 @@
 		case *oop.Indication_PktInd:
 			pktInd := indication.GetPktInd()
 			log.Infow("Received pakcet indication ", log.Fields{"PktInd": pktInd})
+			go dh.handlePacketIndication(pktInd)
 		case *oop.Indication_PortStats:
 			portStats := indication.GetPortStats()
 			log.Infow("Received port stats indication", log.Fields{"PortStats": portStats})
@@ -311,7 +308,7 @@
 		log.Errorw("Device info is nil", log.Fields{})
 		return errors.New("Failed to get device info from OLT")
 	}
-
+	log.Debugw("Fetched device info", log.Fields{"deviceInfo": deviceInfo})
 	dh.device.Root = true
 	dh.device.Vendor = deviceInfo.Vendor
 	dh.device.Model = deviceInfo.Model
@@ -319,7 +316,7 @@
 	dh.device.SerialNumber = deviceInfo.DeviceSerialNumber
 	dh.device.HardwareVersion = deviceInfo.HardwareVersion
 	dh.device.FirmwareVersion = deviceInfo.FirmwareVersion
-	// TODO : Check whether this MAC address is learnt from SDPON or need to send from device
+	// FIXME: Remove Hardcodings
 	dh.device.MacAddress = "0a:0b:0c:0d:0e:0f"
 
 	// Synchronous call to update device - this method is run in its own go routine
@@ -394,10 +391,10 @@
 func (dh *DeviceHandler) omciIndication(omciInd *oop.OmciIndication) error {
 	log.Debugw("omci indication", log.Fields{"intfId": omciInd.IntfId, "onuId": omciInd.OnuId})
 
-	//        ponPort := IntfIdToPortNo(omciInd.GetIntfId(),voltha.Port_PON_OLT)
+	ponPort := IntfIdToPortNo(omciInd.GetIntfId(), voltha.Port_PON_OLT)
 	kwargs := make(map[string]interface{})
 	kwargs["onu_id"] = omciInd.OnuId
-	kwargs["parent_port_no"] = omciInd.GetIntfId()
+	kwargs["parent_port_no"] = ponPort
 
 	if onuDevice, err := dh.coreProxy.GetChildDevice(nil, dh.device.Id, kwargs); err != nil {
 		log.Errorw("onu not found", log.Fields{"intfId": omciInd.IntfId, "onuId": omciInd.OnuId})
@@ -416,7 +413,6 @@
 
 // Process_inter_adapter_message process inter adater message
 func (dh *DeviceHandler) Process_inter_adapter_message(msg *ic.InterAdapterMessage) error {
-	// TODO
 	log.Debugw("Process_inter_adapter_message", log.Fields{"msgId": msg.Header.Id})
 	if msg.Header.Type == ic.InterAdapterMessageType_OMCI_REQUEST {
 		msgId := msg.Header.Id
@@ -462,6 +458,7 @@
 
 func (dh *DeviceHandler) activateONU(intfId uint32, onuId int64, serialNum *oop.SerialNumber, serialNumber string) {
 	log.Debugw("activate-onu", log.Fields{"intfId": intfId, "onuId": onuId, "serialNum": serialNum, "serialNumber": serialNumber})
+	dh.flowMgr.UpdateOnuInfo(intfId, uint32(onuId), serialNumber)
 	// TODO: need resource manager
 	var pir uint32 = 1000000
 	Onu := oop.Onu{IntfId: intfId, OnuId: uint32(onuId), SerialNumber: serialNum, Pir: pir}
@@ -473,10 +470,8 @@
 }
 
 func (dh *DeviceHandler) onuDiscIndication(onuDiscInd *oop.OnuDiscIndication, onuId uint32, sn string) error {
-	//channelId := MkUniPortNum(onuDiscInd.GetIntfId(), onuId, uint32(0))
-	//parentPortNo := IntfIdToPortNo(onuDiscInd.GetIntfId(),voltha.Port_PON_OLT)
 	channelId := onuDiscInd.GetIntfId()
-	parentPortNo := onuDiscInd.GetIntfId()
+	parentPortNo := IntfIdToPortNo(onuDiscInd.GetIntfId(), voltha.Port_PON_OLT)
 	if err := dh.coreProxy.ChildDeviceDetected(nil, dh.device.Id, int(parentPortNo), "brcm_openomci_onu", int(channelId), string(onuDiscInd.SerialNumber.GetVendorId()), sn, int64(onuId)); err != nil {
 		log.Errorw("Create onu error", log.Fields{"parent_id": dh.device.Id, "ponPort": onuDiscInd.GetIntfId(), "onuId": onuId, "sn": sn, "error": err})
 		return err
@@ -484,7 +479,7 @@
 
 	kwargs := make(map[string]interface{})
 	kwargs["onu_id"] = onuId
-	kwargs["parent_port_no"] = onuDiscInd.GetIntfId()
+	kwargs["parent_port_no"] = parentPortNo
 
 	for i := 0; i < 10; i++ {
 		if onuDevice, _ := dh.coreProxy.GetChildDevice(nil, dh.device.Id, kwargs); onuDevice != nil {
@@ -503,19 +498,18 @@
 	serialNumber := dh.stringifySerialNumber(onuInd.SerialNumber)
 
 	kwargs := make(map[string]interface{})
-	//        ponPort := IntfIdToPortNo(onuInd.GetIntfId(),voltha.Port_PON_OLT)
+	ponPort := IntfIdToPortNo(onuInd.GetIntfId(), voltha.Port_PON_OLT)
 
 	if serialNumber != "" {
 		kwargs["serial_number"] = serialNumber
 	} else {
 		kwargs["onu_id"] = onuInd.OnuId
-		kwargs["parent_port_no"] = onuInd.GetIntfId()
+		kwargs["parent_port_no"] = ponPort
 	}
 	if onuDevice, _ := dh.coreProxy.GetChildDevice(nil, dh.device.Id, kwargs); onuDevice != nil {
-		//if intfIdFromPortNo(onuDevice.ParentPortNo) != onuInd.GetIntfId() {
-		if onuDevice.ParentPortNo != onuInd.GetIntfId() {
+		if onuDevice.ParentPortNo != ponPort {
 			//log.Warnw("ONU-is-on-a-different-intf-id-now", log.Fields{"previousIntfId": intfIdFromPortNo(onuDevice.ParentPortNo), "currentIntfId": onuInd.GetIntfId()})
-			log.Warnw("ONU-is-on-a-different-intf-id-now", log.Fields{"previousIntfId": onuDevice.ParentPortNo, "currentIntfId": onuInd.GetIntfId()})
+			log.Warnw("ONU-is-on-a-different-intf-id-now", log.Fields{"previousIntfId": onuDevice.ParentPortNo, "currentIntfId": ponPort})
 		}
 
 		if onuDevice.ProxyAddress.OnuId != onuInd.OnuId {
@@ -608,6 +602,15 @@
 	return onuDevice
 }
 
+func (dh *DeviceHandler) SendPacketInToCore(logicalPort uint32, packetPayload []byte) {
+	log.Debugw("SendPacketInToCore", log.Fields{"port": logicalPort, "packetPayload": packetPayload})
+	if err := dh.coreProxy.SendPacketIn(nil, dh.device.Id, logicalPort, packetPayload); err != nil {
+		log.Errorw("Error sending packetin to core", log.Fields{"error": err})
+		return
+	}
+	log.Debug("Sent packet-in to core successfully")
+}
+
 func (dh *DeviceHandler) UpdateFlowsIncrementally(device *voltha.Device, flows *of.FlowChanges, groups *of.FlowGroupChanges) error {
 	log.Debugw("In UpdateFlowsIncrementally", log.Fields{"deviceId": device.Id, "flows": flows, "groups": groups})
 	if flows != nil {
@@ -683,3 +686,73 @@
 
 	return nil
 }
+
+func (dh *DeviceHandler) handlePacketIndication(packetIn *oop.PacketIndication) {
+	log.Debugw("Received packet-in", log.Fields{"packet-indication": *packetIn})
+	logicalPortNum, err := dh.flowMgr.GetLogicalPortFromPacketIn(packetIn)
+	if err != nil {
+		log.Errorw("Error getting logical port from packet-in", log.Fields{"error": err})
+		return
+	}
+	log.Debugw("sending packet-in to core", log.Fields{"logicalPortNum": logicalPortNum, "packet": *packetIn})
+	if err := dh.coreProxy.SendPacketIn(nil, dh.device.Id, logicalPortNum, packetIn.Pkt); err != nil {
+		log.Errorw("Error sending packet-in to core", log.Fields{"error": err})
+		return
+	}
+	log.Debug("Success sending packet-in to core!")
+}
+
+func (dh *DeviceHandler) PacketOut(egress_port_no int, packet *of.OfpPacketOut) error {
+	log.Debugw("PacketOut", log.Fields{"deviceId": dh.deviceId, "egress_port_no": egress_port_no, "pkt-length": len(packet.Data)})
+	var etherFrame ethernet.Frame
+	err := (&etherFrame).UnmarshalBinary(packet.Data)
+	if err != nil {
+		log.Errorw("Failed to unmarshal into ethernet frame", log.Fields{"err": err, "pkt-length": len(packet.Data)})
+		return err
+	}
+	log.Debugw("Ethernet Frame", log.Fields{"Frame": etherFrame})
+	egressPortType := IntfIdToPortTypeName(uint32(egress_port_no))
+	if egressPortType == voltha.Port_ETHERNET_UNI {
+		if etherFrame.VLAN != nil { // If double tag, remove the outer tag
+			nextEthType := (uint16(packet.Data[16]) << 8) | uint16(packet.Data[17])
+			if nextEthType == 0x8100 {
+				etherFrame.VLAN = nil
+				packet.Data, err = etherFrame.MarshalBinary()
+				if err != nil {
+					log.Fatalf("failed to marshal frame: %v", err)
+					return err
+				}
+				if err := (&etherFrame).UnmarshalBinary(packet.Data); err != nil {
+					log.Fatalf("failed to unmarshal frame: %v", err)
+					return err
+				}
+				log.Debug("Double tagged packet , removed outer vlan", log.Fields{"New frame": etherFrame})
+			}
+		}
+		intfId := IntfIdFromUniPortNum(uint32(egress_port_no))
+		onuId := OnuIdFromPortNum(uint32(egress_port_no))
+		uniId := UniIdFromPortNum(uint32(egress_port_no))
+		/*gemPortId, err := dh.flowMgr.GetPacketOutGemPortId(intfId, onuId, uint32(egress_port_no))
+		  if err != nil{
+		      log.Errorw("Error while getting gemport to packet-out",log.Fields{"error": err})
+		      return err
+		  }*/
+		onuPkt := oop.OnuPacket{IntfId: intfId, OnuId: onuId, PortNo: uint32(egress_port_no), Pkt: packet.Data}
+		log.Debug("sending-packet-to-ONU", log.Fields{"egress_port_no": egress_port_no, "IntfId": intfId, "onuId": onuId,
+			"uniId": uniId, "packet": packet.Data})
+		if _, err := dh.Client.OnuPacketOut(context.Background(), &onuPkt); err != nil {
+			log.Errorw("Error while sending packet-out to ONU", log.Fields{"error": err})
+			return err
+		}
+	} else if egressPortType == voltha.Port_ETHERNET_NNI {
+		uplinkPkt := oop.UplinkPacket{IntfId: IntfIdFromNniPortNum(uint32(egress_port_no)), Pkt: packet.Data}
+		log.Debug("sending-packet-to-uplink", log.Fields{"uplink_pkt": uplinkPkt})
+		if _, err := dh.Client.UplinkPacketOut(context.Background(), &uplinkPkt); err != nil {
+			log.Errorw("Error while sending packet-out to uplink", log.Fields{"error": err})
+			return err
+		}
+	} else {
+		log.Warnw("Packet-out-to-this-interface-type-not-implemented", log.Fields{"egress_port_no": egress_port_no, "egressPortType": egressPortType})
+	}
+	return nil
+}
diff --git a/adaptercore/olt_platform.go b/adaptercore/olt_platform.go
index 131234a..4cc4765 100644
--- a/adaptercore/olt_platform.go
+++ b/adaptercore/olt_platform.go
@@ -129,7 +129,7 @@
 		if (intfId & (1 << 16)) == (1 << 16) {
 			return voltha.Port_ETHERNET_NNI
 		} else {
-			return voltha.Port_UNKNOWN
+			return voltha.Port_ETHERNET_UNI
 		}
 	}
 }
diff --git a/adaptercore/openolt.go b/adaptercore/openolt.go
index 2e5174f..594ceb3 100644
--- a/adaptercore/openolt.go
+++ b/adaptercore/openolt.go
@@ -210,7 +210,7 @@
 	return errors.New("UnImplemented")
 }
 
-func (oo *OpenOLT) Gelete_device(device *voltha.Device) error {
+func (oo *OpenOLT) Delete_device(device *voltha.Device) error {
 	return errors.New("UnImplemented")
 }
 
@@ -235,8 +235,13 @@
 	return errors.New("UnImplemented")
 }
 
-func (oo *OpenOLT) Receive_packet_out(device *voltha.Device, egress_port_no int, msg openflow_13.PacketOut) error {
-	return errors.New("UnImplemented")
+func (oo *OpenOLT) Receive_packet_out(deviceId string, egress_port_no int, packet *openflow_13.OfpPacketOut) error {
+	log.Debugw("Receive_packet_out", log.Fields{"deviceId": deviceId, "egress_port_no": egress_port_no, "pkt": packet})
+	if handler := oo.getDeviceHandler(deviceId); handler != nil {
+		return handler.PacketOut(egress_port_no, packet)
+	}
+	log.Errorw("Receive_packet_out failed-device-handler-not-set", log.Fields{"deviceId": deviceId, "egressport": egress_port_no, "packet": packet})
+	return errors.New("device-handler-not-set")
 }
 
 func (oo *OpenOLT) Suppress_alarm(filter *voltha.AlarmFilter) error {
diff --git a/adaptercore/openolt_flowmgr.go b/adaptercore/openolt_flowmgr.go
index 0b8120b..8d9361e 100644
--- a/adaptercore/openolt_flowmgr.go
+++ b/adaptercore/openolt_flowmgr.go
@@ -80,10 +80,36 @@
 	TRAP_TO_HOST = "trap_to_host"
 )
 
+type onuInfo struct {
+	intfId       uint32
+	onuId        uint32
+	serialNumber string
+}
+
+type onuIdKey struct {
+	intfId uint32
+	onuId  uint32
+}
+
+type gemPortKey struct {
+	intfId  uint32
+	gemPort uint32
+}
+
+type packetInInfoKey struct {
+	intfId      uint32
+	onuId       uint32
+	logicalPort uint32
+}
+
 type OpenOltFlowMgr struct {
-	techprofile   []*tp.TechProfileMgr
-	deviceHandler *DeviceHandler
-	resourceMgr   *rsrcMgr.OpenOltResourceMgr
+	techprofile      []*tp.TechProfileMgr
+	deviceHandler    *DeviceHandler
+	resourceMgr      *rsrcMgr.OpenOltResourceMgr
+	onuIds           map[onuIdKey]onuInfo       //OnuId -> OnuInfo
+	onuSerialNumbers map[string]onuInfo         //onu serial_number (string) -> OnuInfo
+	onuGemPortIds    map[gemPortKey]onuInfo     //GemPortId -> OnuInfo
+	packetInGemPort  map[packetInInfoKey]uint32 //packet in gem port
 }
 
 func NewFlowManager(dh *DeviceHandler, rsrcMgr *rsrcMgr.OpenOltResourceMgr) *OpenOltFlowMgr {
@@ -95,6 +121,10 @@
 		log.Error("Error while populating tech profile mgr\n")
 		return nil
 	}
+	flowMgr.onuIds = make(map[onuIdKey]onuInfo)
+	flowMgr.onuSerialNumbers = make(map[string]onuInfo)
+	flowMgr.onuGemPortIds = make(map[gemPortKey]onuInfo)
+	flowMgr.packetInGemPort = make(map[packetInInfoKey]uint32)
 	log.Info("Initialization of  flow manager success!!")
 	return &flowMgr
 }
@@ -238,6 +268,9 @@
 		log.Error("Errow while uploading gemtopon map to KV store")
 	}
 	log.Debug("Stored tconts and GEM into KV store successfully")
+	for _, gemPort := range gemPortIDs {
+		f.addGemPortToOnuInfoMap(intfId, onuId, gemPort)
+	}
 }
 
 func (f *OpenOltFlowMgr) populateTechProfilePerPonPort() error {
@@ -357,8 +390,8 @@
 	}
 
 	action[TRAP_TO_HOST] = true
-	classifier[UDP_SRC] = 68
-	classifier[UDP_DST] = 67
+	classifier[UDP_SRC] = uint32(68)
+	classifier[UDP_DST] = uint32(67)
 	classifier[PACKET_TAG_TYPE] = SINGLE_TAG
 	delete(classifier, VLAN_VID)
 
@@ -557,7 +590,7 @@
 		classifier.IpProto = ipProto.(uint32)
 	}
 	if vlanId, ok := classifierInfo[VLAN_VID]; ok {
-		classifier.OVid = vlanId.(uint32)
+		classifier.OVid = (vlanId.(uint32)) & 0xFFF
 	}
 	if metadata, ok := classifierInfo[METADATA]; ok { // TODO: Revisit
 		classifier.IVid = uint32(metadata.(uint64))
@@ -647,8 +680,17 @@
 
 func (f *OpenOltFlowMgr) getUpdatedFlowInfo(flow *openolt_pb2.Flow, flowStoreCookie uint64, flowCategory string) *[]rsrcMgr.FlowInfo {
 	var flows []rsrcMgr.FlowInfo = []rsrcMgr.FlowInfo{rsrcMgr.FlowInfo{Flow: flow, FlowCategory: flowCategory, FlowStoreCookie: flowStoreCookie}}
-	// Get existing flow for flowid for given subscriber from KV store
-	existingFlows := f.resourceMgr.GetFlowIDInfo(uint32(flow.AccessIntfId), uint32(flow.OnuId), uint32(flow.UniId), flow.FlowId)
+	var intfId uint32
+	/* For flows which trap out of the NNI, the AccessIntfId is invalid
+	   (set to -1). In such cases, we need to refer to the NetworkIntfId .
+	*/
+	if flow.AccessIntfId != -1 {
+		intfId = uint32(flow.AccessIntfId)
+	} else {
+		intfId = uint32(flow.NetworkIntfId)
+	}
+	// Get existing flows matching flowid for given subscriber from KV store
+	existingFlows := f.resourceMgr.GetFlowIDInfo(intfId, uint32(flow.OnuId), uint32(flow.UniId), flow.FlowId)
 	if existingFlows != nil {
 		log.Debugw("Flow exists for given flowID, appending it to current flow", log.Fields{"flowID": flow.FlowId})
 		for _, f := range *existingFlows {
@@ -709,8 +751,7 @@
 
 func (f *OpenOltFlowMgr) getOnuChildDevice(intfId uint32, onuId uint32) (*voltha.Device, error) {
 	log.Debugw("GetChildDevice", log.Fields{"pon port": intfId, "onuId": onuId})
-	//parentPortNo := IntfIdToPortNo(intfId, voltha.Port_PON_OLT)
-	parentPortNo := intfId
+	parentPortNo := IntfIdToPortNo(intfId, voltha.Port_PON_OLT)
 	onuDevice := f.deviceHandler.GetChildDevice(parentPortNo, onuId)
 	if onuDevice == nil {
 		log.Errorw("onu not found", log.Fields{"intfId": parentPortNo, "onuId": onuId})
@@ -867,6 +908,17 @@
 	}
 	log.Infow("Flow ports", log.Fields{"classifierInfo_inport": classifierInfo[IN_PORT], "action_output": actionInfo[OUTPUT]})
 	portNo, intfId, onuId, uniId := ExtractAccessFromFlow(classifierInfo[IN_PORT].(uint32), actionInfo[OUTPUT].(uint32))
+	if ipProto, ok := classifierInfo[IP_PROTO]; ok {
+		if ipProto.(uint32) == IP_PROTO_DHCP {
+			if udpSrc, ok := classifierInfo[UDP_SRC]; ok {
+				if udpSrc.(uint32) == uint32(67) {
+					log.Debug("trap-dhcp-from-nni-flow")
+					f.addDHCPTrapFlowOnNNI(flow, classifierInfo, portNo)
+					return
+				}
+			}
+		}
+	}
 	f.divideAndAddFlow(intfId, onuId, uniId, portNo, classifierInfo, actionInfo, flow)
 }
 
@@ -898,3 +950,153 @@
 	log.Debugw("success Sending Load-tech-profile-request-to-brcm-onu-adapter", log.Fields{"msg": tpDownloadMsg})
 	return nil
 }
+
+// This function adds onu info to cache
+func (f *OpenOltFlowMgr) UpdateOnuInfo(intfID uint32, onuID uint32, serialNum string) {
+	onu := onuInfo{intfId: intfID, onuId: onuID, serialNumber: serialNum}
+	onuIDkey := onuIdKey{intfId: intfID, onuId: onuID}
+	f.onuIds[onuIDkey] = onu
+	log.Debugw("Updated onuinfo", log.Fields{"intfID": intfID, "onuID": onuID, "serialNum": serialNum})
+}
+
+// This function stores adds GEMport to ONU map
+func (f *OpenOltFlowMgr) addGemPortToOnuInfoMap(intfId uint32, onuId uint32, gemPort uint32) {
+	onuIDkey := onuIdKey{intfId: intfId, onuId: onuId}
+	if val, ok := f.onuIds[onuIDkey]; ok {
+		onuInfo := val
+		gemPortKey := gemPortKey{intfId: intfId, gemPort: gemPort}
+		f.onuGemPortIds[gemPortKey] = onuInfo
+		log.Debugw("Cached Gemport to Onuinfo map", log.Fields{"GemPort": gemPort, "intfId": onuInfo.intfId, "onuId": onuInfo.onuId})
+		return
+	}
+	log.Errorw("OnuInfo not found", log.Fields{"intfId": intfId, "onuId": onuId, "gemPort": gemPort})
+}
+
+// This function Lookup maps  by serialNumber or (intfId, gemPort)
+// Returns OnuID,nil if found or set 0,error if no onuId is found for serialNumber or (intfId, gemPort)
+func (f *OpenOltFlowMgr) getOnuIdfromGemPortMap(serialNumber string, intfId uint32, gemPortId uint32) (uint32, error) {
+	log.Debugw("Getting ONU ID from GEM port and PON port", log.Fields{"serialNumber": serialNumber, "intfId": intfId, "gemPortId": gemPortId})
+	if serialNumber != "" {
+		if onuInfo, ok := f.onuSerialNumbers[serialNumber]; ok {
+			return onuInfo.onuId, nil
+		}
+	} else {
+		gemPortKey := gemPortKey{intfId: intfId, gemPort: gemPortId}
+		if onuInfo, ok := f.onuGemPortIds[gemPortKey]; ok {
+			log.Debugw("Retrived onu info from access", log.Fields{"intfId": intfId, "gemPortId": gemPortId, "onuId": onuInfo.onuId})
+			return onuInfo.onuId, nil
+		}
+	}
+	log.Errorw("ONU ID  is not found", log.Fields{"serialNumber": serialNumber, "intfId": intfId, "gemPort": gemPortId})
+	return uint32(0), errors.New("Key Error ,ONU ID  is not found") // ONU ID 0 is not a valid one
+}
+
+// This function computes logical port UNI/NNI port from packet-in indication and returns the same
+func (f *OpenOltFlowMgr) GetLogicalPortFromPacketIn(packetIn *openolt_pb2.PacketIndication) (uint32, error) {
+	var logicalPortNum uint32
+	var onuId uint32
+	var err error
+
+	if packetIn.IntfType == "pon" {
+		// packet indication does not have serial number , so sending as nil
+		if onuId, err = f.getOnuIdfromGemPortMap("", packetIn.IntfId, packetIn.GemportId); err != nil {
+			log.Errorw("Unable to get ONU ID from GEM/PON port", log.Fields{"pon port": packetIn.IntfId, "gemport": packetIn.GemportId})
+			return logicalPortNum, err
+		}
+		if packetIn.PortNo != 0 {
+			logicalPortNum = packetIn.PortNo
+		} else {
+			uniId := uint32(0) //  FIXME - multi-uni support
+			logicalPortNum = MkUniPortNum(packetIn.IntfId, onuId, uniId)
+		}
+		// Store the gem port through which the packet_in came. Use the same gem port for packet_out
+		pktInkey := packetInInfoKey{intfId: packetIn.IntfId, onuId: onuId, logicalPort: logicalPortNum}
+		f.packetInGemPort[pktInkey] = packetIn.GemportId
+	} else if packetIn.IntfType == "nni" {
+		logicalPortNum = IntfIdToPortNo(packetIn.IntfId, voltha.Port_ETHERNET_NNI)
+	}
+	log.Debugw("Retrieved logicalport from  packet-in", log.Fields{"logicalPortNum": logicalPortNum, "IntfType": packetIn.IntfType})
+	return logicalPortNum, nil
+}
+
+func (f *OpenOltFlowMgr) GetPacketOutGemPortId(intfId uint32, onuId uint32, portNum uint32) (uint32, error) {
+	var gemPortId uint32
+	var err error
+	key := packetInInfoKey{intfId: intfId, onuId: onuId, logicalPort: portNum}
+	if val, ok := f.packetInGemPort[key]; ok {
+		gemPortId = val
+	} else {
+		log.Errorw("Key-Error while fetching packet-out GEM port", log.Fields{"key": key})
+		err = errors.New("Key-Error while fetching packet-out GEM port")
+	}
+	return gemPortId, err
+}
+
+func (f *OpenOltFlowMgr) addDHCPTrapFlowOnNNI(logicalFlow *ofp.OfpFlowStats, classifier map[string]interface{}, portNo uint32) {
+	log.Debug("Adding trap-dhcp-of-nni-flow")
+	action := make(map[string]interface{})
+	classifier[PACKET_TAG_TYPE] = DOUBLE_TAG
+	action[TRAP_TO_HOST] = true
+	/* We manage flowId resource pool on per PON port basis.
+	   Since this situation is tricky, as a hack, we pass the NNI port
+	   index (network_intf_id) as PON port Index for the flowId resource
+	   pool. Also, there is no ONU Id available for trapping DHCP packets
+	   on NNI port, use onu_id as -1 (invalid)
+	   ****************** CAVEAT *******************
+	   This logic works if the NNI Port Id falls within the same valid
+	   range of PON Port Ids. If this doesn't work for some OLT Vendor
+	   we need to have a re-look at this.
+	   *********************************************
+	*/
+	onuId := -1
+	uniId := -1
+	gemPortId := -1
+	allocId := -1
+	networkInterfaceId := DEFAULT_NETWORK_INTERFACE_ID
+	flowStoreCookie := getFlowStoreCookie(classifier, uint32(0))
+	if present := f.resourceMgr.IsFlowCookieOnKVStore(uint32(networkInterfaceId), uint32(onuId), uint32(uniId), flowStoreCookie); present {
+		log.Debug("Flow-exists--not-re-adding")
+		return
+	}
+	flowId, err := f.resourceMgr.GetFlowID(uint32(networkInterfaceId), uint32(onuId), uint32(uniId), flowStoreCookie, "")
+	if err != nil {
+		log.Errorw("Flow id unavailable for DHCP traponNNI flow", log.Fields{"error": err})
+		return
+	}
+	var classifierProto *openolt_pb2.Classifier
+	var actionProto *openolt_pb2.Action
+	if classifierProto = makeOpenOltClassifierField(classifier); classifierProto == nil {
+		log.Error("Error in making classifier protobuf for  dhcp trap on nni flow")
+		return
+	}
+	log.Debugw("Created classifier proto", log.Fields{"classifier": *classifierProto})
+	if actionProto = makeOpenOltActionField(action); actionProto == nil {
+		log.Error("Error in making action protobuf for dhcp trap on nni flow")
+		return
+	}
+	log.Debugw("Created action proto", log.Fields{"action": *actionProto})
+	downstreamflow := openolt_pb2.Flow{AccessIntfId: int32(-1), // AccessIntfId not required
+		OnuId:         int32(onuId), // OnuId not required
+		UniId:         int32(uniId), // UniId not used
+		FlowId:        flowId,
+		FlowType:      DOWNSTREAM,
+		AllocId:       int32(allocId),               // AllocId not used
+		NetworkIntfId: DEFAULT_NETWORK_INTERFACE_ID, // one NNI port is supported now
+		GemportId:     int32(gemPortId),             // GemportId not used
+		Classifier:    classifierProto,
+		Action:        actionProto,
+		Priority:      int32(logicalFlow.Priority),
+		Cookie:        logicalFlow.Cookie,
+		PortNo:        portNo}
+	if ok := f.addFlowToDevice(&downstreamflow); ok {
+		log.Debug("DHCP trap on NNI flow added to device successfully")
+		flowsToKVStore := f.getUpdatedFlowInfo(&downstreamflow, flowStoreCookie, "")
+		if err := f.updateFlowInfoToKVStore(int32(networkInterfaceId),
+			int32(onuId),
+			int32(uniId),
+			flowId, flowsToKVStore); err != nil {
+			log.Errorw("Error uploading DHCP DL  flow into KV store", log.Fields{"flow": downstreamflow, "error": err})
+		}
+	}
+	return
+}
diff --git a/adaptercore/resourcemanager/resourcemanager.go b/adaptercore/resourcemanager/resourcemanager.go
index 2bd84a7..f87ffd1 100755
--- a/adaptercore/resourcemanager/resourcemanager.go
+++ b/adaptercore/resourcemanager/resourcemanager.go
@@ -170,7 +170,7 @@
 		if GlobalPONRsrcMgr == nil {
 			GlobalPONRsrcMgr = RsrcMgrsByTech[technology]
 		}
-		for IntfId := range TechRange.IntfIds {
+		for _, IntfId := range TechRange.IntfIds {
 			ResourceMgr.ResourceMgrs[uint32(IntfId)] = RsrcMgrsByTech[technology]
 		}
 		//self.initialize_device_resource_range_and_pool(resource_mgr, global_resource_mgr, arange)
@@ -222,17 +222,24 @@
 	FlowIDShared := openolt.DeviceInfo_DeviceResourceRanges_Pool_SHARED_BY_ALL_INTF_ALL_TECH // TODO EdgeCore/BAL limitation
 	FlowIDSharedPoolID := uint32(0)
 
-	var GlobalPoolID uint32
 	var FirstIntfPoolID uint32
 	var SharedPoolID uint32
 
+	/*
+	 * As a zero check is made against SharedPoolID to check whether the resources are shared across all intfs
+	 * if resources are shared accross interfaces then SharedPoolID is given a positive number.
+	 */
 	for _, FirstIntfPoolID = range TechRange.IntfIds {
+		// skip the intf id 0
+		if FirstIntfPoolID == 0 {
+			continue
+		}
 		break
 	}
 
 	for _, RangePool := range TechRange.Pools {
 		if RangePool.Sharing == openolt.DeviceInfo_DeviceResourceRanges_Pool_SHARED_BY_ALL_INTF_ALL_TECH {
-			SharedPoolID = GlobalPoolID
+			SharedPoolID = FirstIntfPoolID
 		} else if RangePool.Sharing == openolt.DeviceInfo_DeviceResourceRanges_Pool_SHARED_BY_ALL_INTF_SAME_TECH {
 			SharedPoolID = FirstIntfPoolID
 		} else {
@@ -406,7 +413,6 @@
 		for _, flowId := range FlowIDs {
 			FlowInfo := RsrcMgr.GetFlowIDInfo(PONIntfID, ONUID, UNIID, uint32(flowId))
 			if FlowInfo != nil {
-				log.Debugw("Found flows", log.Fields{"flows": *FlowInfo, "flowId": flowId})
 				for _, Info := range *FlowInfo {
 					if FlowCategory != "" && Info.FlowCategory == FlowCategory {
 						log.Debug("Found flow matching with flow catagory", log.Fields{"flowId": flowId, "FlowCategory": FlowCategory})
@@ -426,7 +432,7 @@
 	if err != nil {
 		log.Errorf("Failed to get resource for interface %d for type %s",
 			PONIntfID, ponrmgr.FLOW_ID)
-		return FlowIDs[0], err
+		return uint32(0), err
 	}
 	if FlowIDs != nil {
 		RsrcMgr.ResourceMgrs[PONIntfID].UpdateFlowIDForOnu(FlowPath, FlowIDs[0], true)
@@ -672,25 +678,25 @@
 	}
 }
 
-/* TODO once the flow id info structure is known
-def is_flow_cookie_on_kv_store(self, intf_id, onu_id, uni_id, flow_store_cookie):
-  '''
-  Note: For flows which trap from the NNI and not really associated with any particular
-  ONU (like LLDP), the onu_id and uni_id is set as -1. The intf_id is the NNI intf_id.
-  '''
-  intf_onu_id = (intf_id, onu_id, uni_id)
-  try:
-      flow_ids = self.resource_mgrs[intf_id]. \
-          get_current_flow_ids_for_onu(intf_onu_id)
-      if flow_ids is not None:
-          for flow_id in flow_ids:
-              flows = self.get_flow_id_info(intf_id, onu_id, uni_id, flow_id)
-              assert (isinstance(flows, list))
-              for flow in flows:
-                  if flow['flow_store_cookie'] == flow_store_cookie:
-                      return True
-  except Exception as e:
-      self.log.error("error-retrieving-flow-info", e=e)
+func (RsrcMgr *OpenOltResourceMgr) IsFlowCookieOnKVStore(PONIntfID uint32, ONUID uint32, UNIID uint32,
+	FlowStoreCookie uint64) bool {
 
-  return False
-*/
+	FlowPath := fmt.Sprintf("%d,%d,%d", PONIntfID, ONUID, UNIID)
+	FlowIDs := RsrcMgr.ResourceMgrs[PONIntfID].GetCurrentFlowIDsForOnu(FlowPath)
+	if FlowIDs != nil {
+		log.Debugw("Found flowId(s) for this ONU", log.Fields{"pon": PONIntfID, "ONUID": ONUID, "UNIID": UNIID, "KVpath": FlowPath})
+		for _, flowId := range FlowIDs {
+			FlowInfo := RsrcMgr.GetFlowIDInfo(PONIntfID, ONUID, UNIID, uint32(flowId))
+			if FlowInfo != nil {
+				log.Debugw("Found flows", log.Fields{"flows": *FlowInfo, "flowId": flowId})
+				for _, Info := range *FlowInfo {
+					if Info.FlowStoreCookie == FlowStoreCookie {
+						log.Debug("Found flow matching with flowStore cookie", log.Fields{"flowId": flowId, "FlowStoreCookie": FlowStoreCookie})
+						return true
+					}
+				}
+			}
+		}
+	}
+	return false
+}