VOL-1878 : Support for LLDP trap flow in OpenOLT adapter

Changes are done to handle LLDP flow implementation.

This commit has dependency on voltha-go, so that needs to
be merged first. Please find the review link
https://gerrit.opencord.org/#/c/15062/

Rebased and fixed sca issue.

Change-Id: I4ce886f68f6f90767eb73cbfa9bd5a25fbcbfb08
diff --git a/adaptercore/olt_platform.go b/adaptercore/olt_platform.go
index 5540392..a75c25d 100644
--- a/adaptercore/olt_platform.go
+++ b/adaptercore/olt_platform.go
@@ -176,12 +176,14 @@
 	return (portNum >> 4) & 0x7F
 }
 
-//FlowExtractInfo fetches uniport from the flow, based on which it gets and returns ponInf, onuID and uniID
-func FlowExtractInfo(flow *ofp.OfpFlowStats, flowDirection string) (uint32, uint32, uint32, uint32, error) {
+//FlowExtractInfo fetches uniport from the flow, based on which it gets and returns ponInf, onuID, uniID, inPort and ethType
+func FlowExtractInfo(flow *ofp.OfpFlowStats, flowDirection string) (uint32, uint32, uint32, uint32, uint32, uint32, error) {
 	var uniPortNo uint32
 	var ponIntf uint32
 	var onuID uint32
 	var uniID uint32
+	var inPort uint32
+	var ethType uint32
 
 	if flowDirection == "upstream" {
 		if uniPortNo = utils.GetChildPortFromTunnelId(flow); uniPortNo == 0 {
@@ -194,24 +196,32 @@
 		}
 	} else if flowDirection == "downstream" {
 		if uniPortNo = utils.GetChildPortFromTunnelId(flow); uniPortNo == 0 {
-			for _, action := range utils.GetActions(flow) {
-				if action.Type == utils.OUTPUT {
-					if out := action.GetOutput(); out != nil {
-						uniPortNo = out.GetPort()
+			for _, field := range utils.GetOfbFields(flow) {
+				if field.GetType() == utils.METADATA {
+					for _, action := range utils.GetActions(flow) {
+						if action.Type == utils.OUTPUT {
+							if out := action.GetOutput(); out != nil {
+								uniPortNo = out.GetPort()
+							}
+							break
+						}
 					}
-					break
+				} else if field.GetType() == utils.IN_PORT {
+					inPort = field.GetPort()
+				} else if field.GetType() == utils.ETH_TYPE {
+					ethType = field.GetEthType()
 				}
 			}
 		}
 	}
 
 	if uniPortNo == 0 {
-		return 0, 0, 0, 0, errors.New("failed to extract Pon Interface, ONU Id and Uni Id from flow")
+		return 0, 0, 0, 0, 0, 0, errors.New("failed to extract Pon Interface, ONU Id and Uni Id from flow")
 	}
 
 	ponIntf = IntfIDFromUniPortNum(uniPortNo)
 	onuID = OnuIDFromUniPortNum(uniPortNo)
 	uniID = UniIDFromPortNum(uniPortNo)
 
-	return uniPortNo, ponIntf, onuID, uniID, nil
+	return uniPortNo, ponIntf, onuID, uniID, inPort, ethType, nil
 }
diff --git a/adaptercore/openolt_flowmgr.go b/adaptercore/openolt_flowmgr.go
index 305f6af..e99d36e 100644
--- a/adaptercore/openolt_flowmgr.go
+++ b/adaptercore/openolt_flowmgr.go
@@ -68,7 +68,7 @@
 
 	//FIXME - see also BRDCM_DEFAULT_VLAN in broadcom_onu.py
 
-	// ReservedVlan Transparent Vlan
+	//ReservedVlan Transparent Vlan
 	ReservedVlan = 4095
 
 	//DefaultMgmtVlan default vlan value
@@ -1044,8 +1044,79 @@
 
 */
 
-func addLLDPFlow(flow *ofp.OfpFlowStats, portNo uint32) {
-	log.Info("unimplemented flow : %v, portNo : %v ", flow, portNo)
+func (f *OpenOltFlowMgr) addLLDPFlow(flow *ofp.OfpFlowStats, portNo uint32) {
+
+	classifierInfo := make(map[string]interface{})
+	actionInfo := make(map[string]interface{})
+
+	classifierInfo[EthType] = uint32(LldpEthType)
+	classifierInfo[PacketTagType] = Untagged
+	actionInfo[TrapToHost] = true
+
+	// LLDP flow is installed to trap LLDP packets on the NNI port.
+	// We manage flow_id 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 flow_id resource
+	// pool. Also, there is no ONU Id available for trapping LLDP 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.
+	// *********************************************
+
+	var onuID = -1
+	var uniID = -1
+	var gemPortID = -1
+
+	var networkInterfaceID = IntfIDFromNniPortNum(portNo)
+	var flowStoreCookie = getFlowStoreCookie(classifierInfo, 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), uint32(gemPortID), flowStoreCookie, "", 0)
+
+	if err != nil {
+		log.Errorw("Flow id unavailable for LLDP traponNNI flow", log.Fields{"error": err})
+		return
+	}
+	var classifierProto *openoltpb2.Classifier
+	var actionProto *openoltpb2.Action
+	if classifierProto = makeOpenOltClassifierField(classifierInfo); classifierProto == nil {
+		log.Error("Error in making classifier protobuf for  LLDP trap on nni flow")
+		return
+	}
+	log.Debugw("Created classifier proto", log.Fields{"classifier": *classifierProto})
+	if actionProto = makeOpenOltActionField(actionInfo); actionProto == nil {
+		log.Error("Error in making action protobuf for LLDP trap on nni flow")
+		return
+	}
+	log.Debugw("Created action proto", log.Fields{"action": *actionProto})
+
+	downstreamflow := openoltpb2.Flow{AccessIntfId: int32(-1), // AccessIntfId not required
+		OnuId:         int32(onuID), // OnuId not required
+		UniId:         int32(uniID), // UniId not used
+		FlowId:        flowID,
+		FlowType:      Downstream,
+		NetworkIntfId: int32(networkInterfaceID),
+		GemportId:     int32(gemPortID),
+		Classifier:    classifierProto,
+		Action:        actionProto,
+		Priority:      int32(flow.Priority),
+		Cookie:        flow.Cookie,
+		PortNo:        portNo}
+	if ok := f.addFlowToDevice(flow, &downstreamflow); ok {
+		log.Debug("LLDP trap on NNI flow added to device successfully")
+		flowsToKVStore := f.getUpdatedFlowInfo(&downstreamflow, flowStoreCookie, "", flowID)
+		if err := f.updateFlowInfoToKVStore(int32(networkInterfaceID),
+			int32(onuID),
+			int32(uniID),
+			flowID, flowsToKVStore); err != nil {
+			log.Errorw("Error uploading LLDP flow into KV store", log.Fields{"flow": downstreamflow, "error": err})
+		}
+	}
+	return
 }
 
 func getUniPortPath(intfID uint32, onuID uint32, uniID uint32) string {
@@ -1083,7 +1154,7 @@
 
 func (f *OpenOltFlowMgr) clearFlowFromResourceManager(flow *ofp.OfpFlowStats, flowID uint32, flowDirection string) {
 	log.Debugw("clearFlowFromResourceManager", log.Fields{"flowID": flowID, "flowDirection": flowDirection, "flow": *flow})
-	portNum, ponIntf, onuID, uniID, err := FlowExtractInfo(flow, flowDirection)
+	portNum, ponIntf, onuID, uniID, inPort, ethType, err := FlowExtractInfo(flow, flowDirection)
 	if err != nil {
 		log.Error(err)
 		return
@@ -1091,6 +1162,16 @@
 	log.Debugw("Extracted access info from flow to be deleted",
 		log.Fields{"ponIntf": ponIntf, "onuID": onuID, "uniID": uniID, "flowID": flowID})
 
+	if ethType == LldpEthType {
+		var networkInterfaceID uint32
+		var onuID = -1
+		var uniID = -1
+
+		networkInterfaceID = IntfIDFromNniPortNum(inPort)
+		f.resourceMgr.FreeFlowID(networkInterfaceID, int32(onuID), int32(uniID), flowID)
+		return
+	}
+
 	flowsInfo := f.resourceMgr.GetFlowIDInfo(ponIntf, onuID, uniID, flowID)
 	if flowsInfo == nil {
 		log.Debugw("No FlowInfo found found in KV store",
@@ -1214,6 +1295,13 @@
 
 	log.Infow("Flow ports", log.Fields{"classifierInfo_inport": classifierInfo[InPort], "action_output": actionInfo[Output]})
 	portNo, intfID, onuID, uniID := ExtractAccessFromFlow(classifierInfo[InPort].(uint32), actionInfo[Output].(uint32))
+	if ethType, ok := classifierInfo[EthType]; ok {
+		if ethType.(uint32) == LldpEthType {
+			log.Info("Adding LLDP flow")
+			f.addLLDPFlow(flow, portNo)
+			return
+		}
+	}
 	if ipProto, ok := classifierInfo[IPProto]; ok {
 		if ipProto.(uint32) == IPProtoDhcp {
 			if udpSrc, ok := classifierInfo[UDPSrc]; ok {
@@ -1520,11 +1608,6 @@
 				installFlowOnAllGemports(nil, f.addEAPOLFlow, args, classifierInfo, actionInfo, flow, gemPorts, EapolFlow, vlanID)
 			}
 		}
-		if ethType == LldpEthType {
-			log.Info("Adding LLDP flow")
-			addLLDPFlow(flow, portNo)
-			return
-		}
 	} else if _, ok := actionInfo[PushVlan]; ok {
 		log.Info("Adding upstream data rule")
 		if pcp, ok := classifierInfo[VlanPcp]; ok {