[VOL-2616]:
- Replicate the flow across all pbits if vlan pcp is not coming as
  part of openflow. Otherwise, only pbit 0 traffic is allowed via
  BAL.
- This is compatible with openolt agent version 2.1.2 and beyond.
  2.1.2 openolt agent version will be released as part of
  https://gerrit.opencord.org/#/c/17601/.

Change-Id: Iec5f5a2fca41e74e1f7f0d2f83149345027b5f18
diff --git a/VERSION b/VERSION
index 00355e2..bc4abe8 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.3.7
+2.3.8
diff --git a/adaptercore/openolt_flowmgr.go b/adaptercore/openolt_flowmgr.go
index 578861d..b79e4bd 100644
--- a/adaptercore/openolt_flowmgr.go
+++ b/adaptercore/openolt_flowmgr.go
@@ -24,6 +24,7 @@
 	"encoding/json"
 	"fmt"
 	"math/big"
+	"strings"
 	"sync"
 	"time"
 
@@ -167,6 +168,11 @@
 	NoneUniID = -1
 	//NoneGemPortID constant
 	NoneGemPortID = -1
+
+	// BinaryStringPrefix is binary string prefix
+	BinaryStringPrefix = "0b"
+	// BinaryBit1 is binary bit 1 expressed as a character
+	BinaryBit1 = '1'
 )
 
 type gemPortKey struct {
@@ -726,12 +732,12 @@
 	log.Debugw("Adding HSIA flow", log.Fields{"intfId": intfID, "onuId": onuID, "uniId": uniID, "classifier": classifier,
 		"action": action, "direction": direction, "allocId": allocID, "gemPortId": gemPortID,
 		"logicalFlow": *logicalFlow})
-	var vlanPbit uint32
+	var vlanPbit uint32 = 0xff // means no pbit
 	if _, ok := classifier[VlanPcp]; ok {
 		vlanPbit = classifier[VlanPcp].(uint32)
 		log.Debugw("Found pbit in the flow", log.Fields{"VlanPbit": vlanPbit})
 	} else {
-		log.Debugw("bpit-not-found-in-flow", log.Fields{"vlan-pcp": VlanPcp})
+		log.Debugw("pbit-not-found-in-flow", log.Fields{"vlan-pcp": VlanPcp})
 	}
 	flowStoreCookie := getFlowStoreCookie(classifier, gemPortID)
 	if present := f.resourceMgr.IsFlowCookieOnKVStore(ctx, uint32(intfID), int32(onuID), int32(uniID), flowStoreCookie); present {
@@ -952,7 +958,7 @@
 }
 
 // Add EAPOL flow to  device with mac, vlanId as classifier for upstream and downstream
-func (f *OpenOltFlowMgr) addEAPOLFlow(ctx context.Context, intfID uint32, onuID uint32, uniID uint32, portNo uint32, logicalFlow *ofp.OfpFlowStats, allocID uint32, gemPortID uint32, vlanID uint32, classifier map[string]interface{}, action map[string]interface{}) error {
+func (f *OpenOltFlowMgr) addEAPOLFlow(ctx context.Context, intfID uint32, onuID uint32, uniID uint32, portNo uint32, classifier map[string]interface{}, action map[string]interface{}, logicalFlow *ofp.OfpFlowStats, allocID uint32, gemPortID uint32, vlanID uint32) error {
 	log.Debugw("Adding EAPOL to device", log.Fields{"intfId": intfID, "onuId": onuID, "portNo": portNo, "allocId": allocID, "gemPortId": gemPortID, "vlanId": vlanID, "flow": logicalFlow})
 
 	uplinkClassifier := make(map[string]interface{})
@@ -1047,12 +1053,11 @@
 			classifier.IVid = vid
 		}
 	}
+	// Use VlanPCPMask (0xff) to signify NO PCP. Else use valid PCP (0 to 7)
 	if vlanPcp, ok := classifierInfo[VlanPcp].(uint32); ok {
-		if vlanPcp == 0 {
-			classifier.OPbits = VlanPCPMask
-		} else {
-			classifier.OPbits = vlanPcp & VlanPCPMask
-		}
+		classifier.OPbits = vlanPcp
+	} else {
+		classifier.OPbits = VlanPCPMask
 	}
 	classifier.SrcPort, _ = classifierInfo[UDPSrc].(uint32)
 	classifier.DstPort, _ = classifierInfo[UDPDst].(uint32)
@@ -2353,23 +2358,40 @@
 		portNo uint32, classifier map[string]interface{}, action map[string]interface{},
 		logicalFlow *ofp.OfpFlowStats, allocId uint32, gemPortId uint32) error,
 	f2 func(ctx context.Context, intfId uint32, onuId uint32, uniId uint32, portNo uint32,
+		classifier map[string]interface{}, action map[string]interface{},
 		logicalFlow *ofp.OfpFlowStats, allocId uint32, gemPortId uint32, vlanId uint32,
-		classifier map[string]interface{}, action map[string]interface{}) error,
+	) error,
 	args map[string]uint32,
 	classifier map[string]interface{}, action map[string]interface{},
 	logicalFlow *ofp.OfpFlowStats,
 	gemPorts []uint32,
+	TpInst *tp.TechProfile,
 	FlowType string,
 	vlanID ...uint32) {
 	log.Debugw("Installing flow on all GEM ports", log.Fields{"FlowType": FlowType, "gemPorts": gemPorts, "vlan": vlanID})
-	for _, gemPortID := range gemPorts {
-		if FlowType == HsiaFlow || FlowType == DhcpFlow {
-			f1(ctx, args["intfId"], args["onuId"], args["uniId"], args["portNo"], classifier, action, logicalFlow, args["allocId"], gemPortID)
-		} else if FlowType == EapolFlow {
-			f2(ctx, args["intfId"], args["onuId"], args["uniId"], args["portNo"], logicalFlow, args["allocId"], gemPortID, vlanID[0], classifier, action)
-		} else {
-			log.Errorw("Unrecognized Flow Type", log.Fields{"FlowType": FlowType})
-			return
+
+	for _, gemPortAttribute := range TpInst.UpstreamGemPortAttributeList {
+		var gemPortID uint32
+		// The bit mapping for a gemport is expressed in tech-profile as a binary string. For example, 0b00000001
+		// We need to trim prefix "0b", before further processing
+		// Once the "0b" prefix is trimmed, we iterate each character in the string to identify which index
+		// in the string is set to binary bit 1 (expressed as char '1' in the binary string).
+		for pos, pbitSet := range strings.TrimPrefix(gemPortAttribute.PbitMap, BinaryStringPrefix) {
+			// If a particular character in the string is set to '1', identify the index of this character from
+			// the LSB position which marks the PCP bit consumed by the given gem port.
+			// This PCP bit now becomes a classifier in the flow.
+			if pbitSet == BinaryBit1 {
+				classifier[VlanPcp] = uint32(len(strings.TrimPrefix(gemPortAttribute.PbitMap, BinaryStringPrefix))) - 1 - uint32(pos)
+				gemPortID = gemPortAttribute.GemportID
+				if FlowType == HsiaFlow || FlowType == DhcpFlow || FlowType == IgmpFlow {
+					f1(ctx, args["intfId"], args["onuId"], args["uniId"], args["portNo"], classifier, action, logicalFlow, args["allocId"], gemPortID)
+				} else if FlowType == EapolFlow {
+					f2(ctx, args["intfId"], args["onuId"], args["uniId"], args["portNo"], classifier, action, logicalFlow, args["allocId"], gemPortID, vlanID[0])
+				} else {
+					log.Errorw("Unrecognized Flow Type", log.Fields{"FlowType": FlowType})
+					return
+				}
+			}
 		}
 	}
 }
@@ -2592,7 +2614,7 @@
 				f.addDHCPTrapFlow(ctx, intfID, onuID, uniID, portNo, classifierInfo, actionInfo, flow, allocID, gemPort)
 			} else {
 				//Adding DHCP upstream flow to all gemports
-				installFlowOnAllGemports(ctx, f.addDHCPTrapFlow, nil, args, classifierInfo, actionInfo, flow, gemPorts, DhcpFlow)
+				installFlowOnAllGemports(ctx, f.addDHCPTrapFlow, nil, args, classifierInfo, actionInfo, flow, gemPorts, TpInst, DhcpFlow)
 			}
 
 		} else if ipProto == IgmpProto {
@@ -2604,7 +2626,7 @@
 				f.addIGMPTrapFlow(ctx, intfID, onuID, uniID, portNo, classifierInfo, actionInfo, flow, allocID, gemPort)
 			} else {
 				//Adding IGMP upstream flow to all gem ports
-				installFlowOnAllGemports(ctx, f.addIGMPTrapFlow, nil, args, classifierInfo, actionInfo, flow, gemPorts, IgmpFlow)
+				installFlowOnAllGemports(ctx, f.addIGMPTrapFlow, nil, args, classifierInfo, actionInfo, flow, gemPorts, TpInst, IgmpFlow)
 			}
 		} else {
 			log.Errorw("Invalid-Classifier-to-handle", log.Fields{"classifier": classifierInfo, "action": actionInfo})
@@ -2624,9 +2646,9 @@
 					tp_pb.Direction_UPSTREAM,
 					pcp.(uint32))
 
-				f.addEAPOLFlow(ctx, intfID, onuID, uniID, portNo, flow, allocID, gemPort, vlanID, classifierInfo, actionInfo)
+				f.addEAPOLFlow(ctx, intfID, onuID, uniID, portNo, classifierInfo, actionInfo, flow, allocID, gemPort, vlanID)
 			} else {
-				installFlowOnAllGemports(ctx, nil, f.addEAPOLFlow, args, classifierInfo, actionInfo, flow, gemPorts, EapolFlow, vlanID)
+				installFlowOnAllGemports(ctx, nil, f.addEAPOLFlow, args, classifierInfo, actionInfo, flow, gemPorts, TpInst, EapolFlow, vlanID)
 			}
 		}
 	} else if _, ok := actionInfo[PushVlan]; ok {
@@ -2639,7 +2661,7 @@
 			f.addUpstreamDataFlow(ctx, intfID, onuID, uniID, portNo, classifierInfo, actionInfo, flow, allocID, gemPort)
 		} else {
 			//Adding HSIA upstream flow to all gemports
-			installFlowOnAllGemports(ctx, f.addUpstreamDataFlow, nil, args, classifierInfo, actionInfo, flow, gemPorts, HsiaFlow)
+			installFlowOnAllGemports(ctx, f.addUpstreamDataFlow, nil, args, classifierInfo, actionInfo, flow, gemPorts, TpInst, HsiaFlow)
 		}
 	} else if _, ok := actionInfo[PopVlan]; ok {
 		log.Info("Adding Downstream data rule")
@@ -2651,7 +2673,7 @@
 			f.addDownstreamDataFlow(ctx, intfID, onuID, uniID, portNo, classifierInfo, actionInfo, flow, allocID, gemPort)
 		} else {
 			//Adding HSIA downstream flow to all gemports
-			installFlowOnAllGemports(ctx, f.addDownstreamDataFlow, nil, args, classifierInfo, actionInfo, flow, gemPorts, HsiaFlow)
+			installFlowOnAllGemports(ctx, f.addDownstreamDataFlow, nil, args, classifierInfo, actionInfo, flow, gemPorts, TpInst, HsiaFlow)
 		}
 	} else {
 		log.Errorw("Invalid-flow-type-to-handle", log.Fields{"classifier": classifierInfo, "action": actionInfo, "flow": flow})