[VOL-4439] Support transparent VLAN at the OLT.

Change-Id: Iac79acb1a380a807ed589c2c48d128a7c3fe891b
diff --git a/VERSION b/VERSION
index ad6abd3..1454f6e 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-4.0.1-dev
+4.0.1
diff --git a/internal/pkg/core/openolt_flowmgr.go b/internal/pkg/core/openolt_flowmgr.go
index 82c77f6..8699740 100644
--- a/internal/pkg/core/openolt_flowmgr.go
+++ b/internal/pkg/core/openolt_flowmgr.go
@@ -968,7 +968,7 @@
 }
 
 func (f *OpenOltFlowMgr) addUpstreamDataPathFlow(ctx context.Context, flowContext *flowContext) error {
-	flowContext.classifier[PacketTagType] = SingleTag
+	flowContext.classifier[PacketTagType] = SingleTag // FIXME: This hardcoding needs to be removed.
 	logger.Debugw(ctx, "adding-upstream-data-flow",
 		log.Fields{
 			"uplinkClassifier": flowContext.classifier,
@@ -981,7 +981,16 @@
 	downlinkClassifier := flowContext.classifier
 	downlinkAction := flowContext.action
 
-	downlinkClassifier[PacketTagType] = DoubleTag
+	// 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
+	}
 	logger.Debugw(ctx, "adding-downstream-data-flow",
 		log.Fields{
 			"downlinkClassifier": downlinkClassifier,
@@ -1003,17 +1012,10 @@
 		}
 	}
 
-	/* Already this info available classifier? */
-	downlinkAction[PopVlan] = true
 	// 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
-	} else {
-		return olterrors.NewErrInvalidValue(log.Fields{
-			"reason":    "failed-to-convert-vlanid-classifier",
-			"vlan-id":   VlanVid,
-			"device-id": f.deviceHandler.device.Id}, nil).Log()
 	}
 
 	return f.addSymmetricDataPathFlow(ctx, flowContext, Downstream)
@@ -1403,10 +1405,21 @@
 			classifier.OVid = vid
 		}
 	}
+	// The classifierInfo[Metadata] carries the vlan that the OLT see when it receives packet from the ONU
 	if metadata, ok := classifierInfo[Metadata].(uint64); ok {
 		vid := uint32(metadata)
-		if vid != ReservedVlan {
-			classifier.IVid = vid
+		// Set the OVid or IVid classifier based on the whether OLT is using a transparent tag or not
+		// If OLT is using transparent tag mechanism, then it classifies whatever tag it sees to/from ONU which
+		//is OVid from the perspective of the OLT. When OLT also places or pops the outer tag, then classifierInfo[Metadata]
+		// becomes the IVid.
+		if classifier.OVid != 0 && classifier.OVid != ReservedVlan { // This is case when classifier.OVid is not set
+			if vid != ReservedVlan {
+				classifier.IVid = vid
+			}
+		} else {
+			if vid != ReservedVlan {
+				classifier.OVid = vid
+			}
 		}
 	}
 	// Use VlanPCPMask (0xff) to signify NO PCP. Else use valid PCP (0 to 7)
@@ -1461,9 +1474,13 @@
 		}
 	} else if _, ok := actionInfo[TrapToHost]; ok {
 		action.Cmd.TrapToHost = actionInfo[TrapToHost].(bool)
-	} else {
-		return nil, olterrors.NewErrInvalidValue(log.Fields{"action-command": actionInfo}, nil)
 	}
+	// When OLT is transparent to vlans no-action is valid.
+	/*
+		else {
+			return nil, olterrors.NewErrInvalidValue(log.Fields{"action-command": actionInfo}, nil)
+		}
+	*/
 	return &action, nil
 }
 
@@ -3029,8 +3046,11 @@
 			classifierInfo[InPort] = field.GetPort()
 			logger.Debug(ctx, "field-type-in-port", log.Fields{"classifierInfo[IN_PORT]": classifierInfo[InPort].(uint32)})
 		} else if field.Type == flows.VLAN_VID {
-			classifierInfo[VlanVid] = field.GetVlanVid() & 0xfff
-			logger.Debug(ctx, "field-type-vlan-vid", log.Fields{"classifierInfo[VLAN_VID]": classifierInfo[VlanVid].(uint32)})
+			// The ReservedVlan is used to signify transparent vlan. Do not do any classification when we see ReservedVlan
+			if field.GetVlanVid() != ReservedVlan {
+				classifierInfo[VlanVid] = field.GetVlanVid() & 0xfff
+				logger.Debug(ctx, "field-type-vlan-vid", log.Fields{"classifierInfo[VLAN_VID]": classifierInfo[VlanVid].(uint32)})
+			}
 		} else if field.Type == flows.VLAN_PCP {
 			classifierInfo[VlanPcp] = field.GetVlanPcp()
 			logger.Debug(ctx, "field-type-vlan-pcp", log.Fields{"classifierInfo[VLAN_PCP]": classifierInfo[VlanPcp].(uint32)})