VOL-4625: Support for FTTB flows in openolt adapter

Change-Id: Icdf3c66f310f68101e8a84ef272ed38b849d77bf
diff --git a/internal/pkg/core/openolt_flowmgr.go b/internal/pkg/core/openolt_flowmgr.go
index 8b24630..06ad2c0 100644
--- a/internal/pkg/core/openolt_flowmgr.go
+++ b/internal/pkg/core/openolt_flowmgr.go
@@ -91,6 +91,8 @@
 	EthType = "eth_type"
 	//EthDst constant
 	EthDst = "eth_dst"
+	//EthSrc constant
+	EthSrc = "eth_src"
 	//TPID constant
 	TPID = "tpid"
 	//IPProto constant
@@ -978,28 +980,38 @@
 }
 
 func (f *OpenOltFlowMgr) addUpstreamDataPathFlow(ctx context.Context, flowContext *flowContext) error {
-	flowContext.classifier[PacketTagType] = SingleTag // FIXME: This hardcoding needs to be removed.
+	flowContext.classifier[PacketTagType] = SingleTag
+	// extract the cvid/inner-vid from the write metadata
+	writeMetadata := flows.GetMetadataFromWriteMetadataAction(ctx, flowContext.logicalFlow)
+	if writeMetadata != 0 {
+		// Writemetadata field is 8 bytes
+		// cvid is on the outer most two bytes of the write metadata
+		cvid := (writeMetadata & 0xffff000000000000) >> 48
+		// if the cvid does not match the classifier vlan, then this indicates it is a double tagged packet
+		if cvid != flowContext.classifier[VlanVid] && cvid != 0 {
+			flowContext.classifier[PacketTagType] = DoubleTag
+		}
+	}
 	logger.Debugw(ctx, "adding-upstream-data-flow",
 		log.Fields{
 			"uplinkClassifier": flowContext.classifier,
 			"uplinkAction":     flowContext.action})
 	return f.addSymmetricDataPathFlow(ctx, flowContext, Upstream)
-	/* TODO: Install Secondary EAP on the subscriber vlan */
 }
 
 func (f *OpenOltFlowMgr) addDownstreamDataPathFlow(ctx context.Context, flowContext *flowContext) error {
 	downlinkClassifier := flowContext.classifier
 	downlinkAction := flowContext.action
-
-	// TODO: For now mark the PacketTagType as SingleTag when OLT is transparent to VLAN
-	// Per some deployment models, it is also possible that ONU operates on double tagged packets,
-	// so this hardcoding of SingeTag packet-tag-type may be problem for such deployment models.
-	// Need a better way for detection of packet tag type from OpenFlow message.
-	if _, ok := downlinkClassifier[VlanVid]; !ok {
-		downlinkClassifier[PacketTagType] = SingleTag
-	} else {
-		downlinkClassifier[PacketTagType] = DoubleTag
-		downlinkAction[PopVlan] = true
+	// default to single tag. If we detect an inner cvid from write-metadata, then we mark the PacketTagType as DoubleTag
+	downlinkClassifier[PacketTagType] = SingleTag
+	// extract the cvid/inner-vid from the write metadata
+	writeMetadata := flows.GetMetadataFromWriteMetadataAction(ctx, flowContext.logicalFlow)
+	if writeMetadata != 0 {
+		// Writemetadata field is 8 bytes
+		// cvid is on the outer most two bytes of the write metadata
+		if cvid := (writeMetadata & 0xffff000000000000) >> 48; cvid != 0 {
+			downlinkClassifier[PacketTagType] = DoubleTag
+		}
 	}
 	logger.Debugw(ctx, "adding-downstream-data-flow",
 		log.Fields{
@@ -1022,10 +1034,14 @@
 		}
 	}
 
-	// vlan_vid is a uint32.  must be type asserted as such or conversion fails
-	dlClVid, ok := downlinkClassifier[VlanVid].(uint32)
-	if ok {
-		downlinkAction[VlanVid] = dlClVid & 0xfff
+	// If Pop Vlan action is specified, set the vlan to be popped from the classifier vlan match field.
+	// The matched vlan is the one that is getting popped.
+	if val, ok := downlinkAction[PopVlan]; ok && val.(bool) {
+		// vlan_vid is a uint32.  must be type asserted as such or conversion fails
+		dlClVid, ok := downlinkClassifier[VlanVid].(uint32)
+		if ok {
+			downlinkAction[VlanVid] = dlClVid & 0xfff
+		}
 	}
 
 	return f.addSymmetricDataPathFlow(ctx, flowContext, Downstream)
@@ -1464,6 +1480,7 @@
 	classifier.DstIp, _ = classifierInfo[Ipv4Dst].(uint32)
 	classifier.SrcIp, _ = classifierInfo[Ipv4Src].(uint32)
 	classifier.DstMac, _ = classifierInfo[EthDst].([]uint8)
+	classifier.SrcMac, _ = classifierInfo[EthSrc].([]uint8)
 	if pktTagType, ok := classifierInfo[PacketTagType].(string); ok {
 		classifier.PktTagType = pktTagType
 
@@ -1483,28 +1500,38 @@
 	var action openoltpb2.Action
 	action.Cmd = &actionCmd
 	if _, ok := actionInfo[PopVlan]; ok {
+		// Pop outer vid
 		action.Cmd.RemoveOuterTag = true
 		if _, ok := actionInfo[VlanPcp]; ok {
+			// Remark inner pbit
 			action.Cmd.RemarkInnerPbits = true
 			action.IPbits = actionInfo[VlanPcp].(uint32)
 			if _, ok := actionInfo[VlanVid]; ok {
+				// Remark inner vid
 				action.Cmd.TranslateInnerTag = true
 				action.IVid = actionInfo[VlanVid].(uint32)
 			}
 		}
 	} else if _, ok := actionInfo[PushVlan]; ok {
+		// push outer vid
 		action.OVid = actionInfo[VlanVid].(uint32)
 		action.Cmd.AddOuterTag = true
 		if _, ok := actionInfo[VlanPcp]; ok {
+			// translate outer pbit
 			action.OPbits = actionInfo[VlanPcp].(uint32)
 			action.Cmd.RemarkOuterPbits = true
 			if _, ok := classifierInfo[VlanVid]; ok {
+				// translate inner vid
 				action.IVid = classifierInfo[VlanVid].(uint32)
 				action.Cmd.TranslateInnerTag = true
 			}
 		}
 	} else if _, ok := actionInfo[TrapToHost]; ok {
 		action.Cmd.TrapToHost = actionInfo[TrapToHost].(bool)
+	} else if _, ok := actionInfo[VlanVid]; ok {
+		// Translate outer vid
+		action.Cmd.TranslateOuterTag = true
+		action.OVid = actionInfo[VlanVid].(uint32)
 	}
 	// When OLT is transparent to vlans no-action is valid.
 	/*
@@ -2965,7 +2992,10 @@
 			logger.Debug(ctx, "field-type-eth-type", log.Fields{"classifierInfo[ETH_TYPE]": classifierInfo[EthType].(uint32)})
 		} else if field.Type == flows.ETH_DST {
 			classifierInfo[EthDst] = field.GetEthDst()
-			logger.Debug(ctx, "field-type-eth-type", log.Fields{"classifierInfo[ETH_DST]": classifierInfo[EthDst].([]uint8)})
+			logger.Debug(ctx, "field-type-eth-dst", log.Fields{"classifierInfo[ETH_DST]": classifierInfo[EthDst].([]uint8)})
+		} else if field.Type == flows.ETH_SRC {
+			classifierInfo[EthSrc] = field.GetEthSrc()
+			logger.Debug(ctx, "field-type-eth-src", log.Fields{"classifierInfo[ETH_SRC]": classifierInfo[EthSrc].([]uint8)})
 		} else if field.Type == flows.IP_PROTO {
 			classifierInfo[IPProto] = field.GetIpProto()
 			logger.Debug(ctx, "field-type-ip-proto", log.Fields{"classifierInfo[IP_PROTO]": classifierInfo[IPProto].(uint32)})
diff --git a/internal/pkg/core/openolt_flowmgr_test.go b/internal/pkg/core/openolt_flowmgr_test.go
index 218ecf5..0c94e94 100644
--- a/internal/pkg/core/openolt_flowmgr_test.go
+++ b/internal/pkg/core/openolt_flowmgr_test.go
@@ -1203,6 +1203,16 @@
 	kwTable0Meter1["meter_id"] = 1
 	kwTable0Meter1["write_metadata"] = 0x4000000000 // Tech-Profile-ID 64
 
+	kwTable0Meter1WithCvid := make(map[string]uint64)
+	kwTable0Meter1WithCvid["table_id"] = 0
+	kwTable0Meter1WithCvid["meter_id"] = 1
+	kwTable0Meter1WithCvid["write_metadata"] = 0x3c004000000000 // Tech-Profile-ID 64, cvid 60 in write metadata
+
+	kwTable1Meter1WithCvid := make(map[string]uint64)
+	kwTable1Meter1WithCvid["table_id"] = 1
+	kwTable1Meter1WithCvid["meter_id"] = 1
+	kwTable1Meter1WithCvid["write_metadata"] = 0x3c004000000000 // Tech-Profile-ID 64, cvid 60 in write metadata
+
 	flowMetadata1 := ofp.FlowMetadata{Meters: []*ofp.OfpMeterConfig{
 		{
 			Flags:   5,
@@ -1388,6 +1398,37 @@
 		},
 		KV: kwTable1Meter1,
 	}
+
+	// Downstream FTTB Subscriber flow - ONU1 UNI0 PON0, write metadata with cvid
+	// match outer vid 100, with inner cvid 60 -> modify outer vid to 200 and output on pon port
+	fa9 := &fu.FlowArgs{
+		MatchFields: []*ofp.OfpOxmOfbField{
+			fu.InPort(16777216),
+			fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT | 100)),
+			fu.TunnelId(256),
+		},
+		Actions: []*ofp.OfpAction{
+			fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT | 200))),
+			fu.Output(536870912),
+		},
+		KV: kwTable0Meter1WithCvid,
+	}
+
+	// Upstream FTTB Subscriber flow - ONU1 UNI0 PON0, write metadata with cvid
+	// match outer vid 200, with inner cvid 60 -> modify outer vid to 100 and output on nni port
+	fa10 := &fu.FlowArgs{
+		MatchFields: []*ofp.OfpOxmOfbField{
+			fu.InPort(536870912),
+			fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT | 200)),
+			fu.TunnelId(256),
+		},
+		Actions: []*ofp.OfpAction{
+			fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT | 100))),
+			fu.Output(2147483645),
+		},
+		KV: kwTable1Meter1WithCvid,
+	}
+
 	flow0, _ := fu.MkFlowStat(fa0)
 	flow1, _ := fu.MkFlowStat(fa1)
 	flow2, _ := fu.MkFlowStat(fa2)
@@ -1399,6 +1440,9 @@
 	flow7, _ := fu.MkFlowStat(fa7)
 	flow8, _ := fu.MkFlowStat(fa8)
 
+	flow9, _ := fu.MkFlowStat(fa9)
+	flow10, _ := fu.MkFlowStat(fa10)
+
 	type args struct {
 		ctx          context.Context
 		intfID       int32
@@ -1544,6 +1588,28 @@
 			},
 			wantErr: false,
 		},
+		{
+			name: "RouteFlowToOnuChannel-fttb-subscriber-downstream-12",
+			args: args{
+				ctx:          ctx,
+				intfID:       0,
+				flow:         flow9,
+				addFlow:      true,
+				flowMetadata: &flowMetadata1,
+			},
+			wantErr: false,
+		},
+		{
+			name: "RouteFlowToOnuChannel-fttb-subscriber-upstream-13",
+			args: args{
+				ctx:          ctx,
+				intfID:       0,
+				flow:         flow10,
+				addFlow:      true,
+				flowMetadata: &flowMetadata1,
+			},
+			wantErr: false,
+		},
 	}
 
 	var wg sync.WaitGroup