VOL-3614  Other multicast ME created after multicast gem detected.

Change-Id: Idc272aa9f6ae3c8da3910031bc226847f253a78f
diff --git a/VERSION b/VERSION
index 736f74c..e515aee 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.1.15-dev146
+0.1.15-dev147
diff --git a/go.mod b/go.mod
index c315675..2a56590 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,7 @@
 	github.com/golang/protobuf v1.4.2
 	github.com/google/gopacket v1.1.17
 	github.com/looplab/fsm v0.1.0
-	github.com/opencord/omci-lib-go v0.13.3
+	github.com/opencord/omci-lib-go v0.13.4
 	github.com/opencord/voltha-lib-go/v3 v3.1.23
 	github.com/opencord/voltha-protos/v3 v3.3.9
 	github.com/stretchr/testify v1.6.1
diff --git a/go.sum b/go.sum
index 89c8c62..ca7f92e 100644
--- a/go.sum
+++ b/go.sum
@@ -217,8 +217,8 @@
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/gomega v1.4.2 h1:3mYCb7aPxS/RU7TI1y4rkEn1oKmPRjNJLNEXgw7MH2I=
 github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/opencord/omci-lib-go v0.13.3 h1:HlBw/bdCsfz/sRVm8bXNrxdRcCpPqEqJ7xQk7CUqWkA=
-github.com/opencord/omci-lib-go v0.13.3/go.mod h1:6OIHB14Ch5qGgHzwSWlMACtk5KFoLzQ4LAhdcy4jwvo=
+github.com/opencord/omci-lib-go v0.13.4 h1:L3GpFHrx3DASixkCvztnJbcLGUTU/21wJYdE/qG9/XA=
+github.com/opencord/omci-lib-go v0.13.4/go.mod h1:6OIHB14Ch5qGgHzwSWlMACtk5KFoLzQ4LAhdcy4jwvo=
 github.com/opencord/voltha-lib-go/v3 v3.1.23 h1:cbrg+QmIXR3fQHSlo5+QokmSDkFdU7fTYmwxCU5QprY=
 github.com/opencord/voltha-lib-go/v3 v3.1.23/go.mod h1:sa508HZ5vlOauh0i+WC0XFX1JZnfHtJqNIms5XBT/Z0=
 github.com/opencord/voltha-protos/v3 v3.3.9 h1:BnfDN9oaRBgyAiH9ZN7LpBpEJYxjX/ZS7R4OT2hDrtY=
diff --git a/internal/pkg/onuadaptercore/device_handler.go b/internal/pkg/onuadaptercore/device_handler.go
index 8c46421..8349d51 100644
--- a/internal/pkg/onuadaptercore/device_handler.go
+++ b/internal/pkg/onuadaptercore/device_handler.go
@@ -2443,3 +2443,13 @@
 	}
 	return nil
 }
+
+// getUniPortMEEntityID takes uniPortNo as the input and returns the Entity ID corresponding to this UNI-G ME Instance
+func (dh *deviceHandler) getUniPortMEEntityID(uniPortNo uint32) (uint16, error) {
+	dh.lockDevice.RLock()
+	defer dh.lockDevice.RUnlock()
+	if uniPort, ok := dh.uniEntityMap[uniPortNo]; ok {
+		return uniPort.entityID, nil
+	}
+	return 0, errors.New("error-fetching-uni-port")
+}
diff --git a/internal/pkg/onuadaptercore/mib_download.go b/internal/pkg/onuadaptercore/mib_download.go
index 48abc65..cc39da0 100644
--- a/internal/pkg/onuadaptercore/mib_download.go
+++ b/internal/pkg/onuadaptercore/mib_download.go
@@ -158,7 +158,7 @@
 				return
 			}
 			logger.Debugw("CreateResponse Data", log.Fields{"device-id": onuDeviceEntry.deviceID, "data-fields": msgObj})
-			if msgObj.Result != me.Success {
+			if msgObj.Result != me.Success && msgObj.Result != me.InstanceExists {
 				logger.Errorw("Omci CreateResponse Error - later: drive FSM to abort state ?", log.Fields{"device-id": onuDeviceEntry.deviceID, "Error": msgObj.Result})
 				// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
 				return
diff --git a/internal/pkg/onuadaptercore/omci_ani_config.go b/internal/pkg/onuadaptercore/omci_ani_config.go
index 70b5f67..2cef989 100644
--- a/internal/pkg/onuadaptercore/omci_ani_config.go
+++ b/internal/pkg/onuadaptercore/omci_ani_config.go
@@ -307,10 +307,10 @@
 		mapGemPortParams := oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].mapGemPortParams
 		oFsm.pUniTechProf.mutexTPState.Unlock()
 
-		loGemPortAttribs := ponAniGemPortAttribs{}
 		//for all TechProfile set GemIndices
-
 		for _, gemEntry := range mapGemPortParams {
+			loGemPortAttribs := ponAniGemPortAttribs{}
+
 			//collect all GemConfigData in a separate Fsm related slice (needed also to avoid mix-up with unsorted mapPonAniConfig)
 
 			if queueInstKeys := oFsm.pOnuDB.getSortedInstKeys(me.PriorityQueueClassID); len(queueInstKeys) > 0 {
@@ -368,23 +368,30 @@
 			loGemPortAttribs.weight = gemEntry.queueWeight
 			loGemPortAttribs.pbitString = gemEntry.pbitString
 			if gemEntry.isMulticast {
+				//TODO this might effectively ignore the for loop starting at line 316
+				loGemPortAttribs.gemPortID = gemEntry.multicastGemPortID
 				loGemPortAttribs.isMulticast = true
 				loGemPortAttribs.multicastGemID = gemEntry.multicastGemPortID
 				loGemPortAttribs.staticACL = gemEntry.staticACL
 				loGemPortAttribs.dynamicACL = gemEntry.dynamicACL
-			}
 
-			logger.Debugw("prio-related GemPort attributes:", log.Fields{
-				"gemPortID":      loGemPortAttribs.gemPortID,
-				"upQueueID":      loGemPortAttribs.upQueueID,
-				"downQueueID":    loGemPortAttribs.downQueueID,
-				"pbitString":     loGemPortAttribs.pbitString,
-				"prioQueueIndex": gemEntry.prioQueueIndex,
-				"isMulticast":    loGemPortAttribs.isMulticast,
-				"multicastGemID": loGemPortAttribs.multicastGemID,
-				"staticACL":      loGemPortAttribs.staticACL,
-				"dynamicACL":     loGemPortAttribs.dynamicACL,
-			})
+				logger.Debugw("Multicast GemPort attributes:", log.Fields{
+					"gemPortID":      loGemPortAttribs.gemPortID,
+					"isMulticast":    loGemPortAttribs.isMulticast,
+					"multicastGemID": loGemPortAttribs.multicastGemID,
+					"staticACL":      loGemPortAttribs.staticACL,
+					"dynamicACL":     loGemPortAttribs.dynamicACL,
+				})
+
+			} else {
+				logger.Debugw("Upstream GemPort attributes:", log.Fields{
+					"gemPortID":      loGemPortAttribs.gemPortID,
+					"upQueueID":      loGemPortAttribs.upQueueID,
+					"downQueueID":    loGemPortAttribs.downQueueID,
+					"pbitString":     loGemPortAttribs.pbitString,
+					"prioQueueIndex": gemEntry.prioQueueIndex,
+				})
+			}
 
 			oFsm.gemPortAttribsSlice = append(oFsm.gemPortAttribsSlice, loGemPortAttribs)
 		}
@@ -493,6 +500,7 @@
 }
 
 func (oFsm *uniPonAniConfigFsm) enterSettingDot1PMapper(e *fsm.Event) {
+
 	logger.Debugw("uniPonAniConfigFsm Tx Set::.1pMapper with all PBits set", log.Fields{"EntitytId": 0x8042, /*cmp above*/
 		"toGemIw":   1024, /* cmp above */
 		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.uniID})
@@ -509,6 +517,18 @@
 	//assign the GemPorts according to the configured Prio
 	var loPrioGemPortArray [8]uint16
 	for _, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+		if gemPortAttribs.isMulticast {
+			logger.Debugw("uniPonAniConfigFsm Port is Multicast, ignoring .1pMapper", log.Fields{
+				"device-id": oFsm.deviceID, "GemPort": gemPortAttribs.gemPortID,
+				"prioString": gemPortAttribs.pbitString})
+			continue
+		}
+		if gemPortAttribs.pbitString == "" {
+			logger.Warnw("uniPonAniConfigFsm PrioString empty string error", log.Fields{
+				"device-id": oFsm.deviceID, "GemPort": gemPortAttribs.gemPortID,
+				"prioString": gemPortAttribs.pbitString})
+			continue
+		}
 		for i := 0; i < 8; i++ {
 			// "lenOfPbitMap(8) - i + 1" will give i-th pbit value from LSB position in the pbit map string
 			if prio, err := strconv.Atoi(string(gemPortAttribs.pbitString[7-i])); err == nil {
@@ -529,39 +549,58 @@
 
 		}
 	}
+
 	var foundIwPtr = false
 	for index, value := range loPrioGemPortArray {
+		meAttribute := fmt.Sprintf("InterworkTpPointerForPBitPriority%d", index)
 		if value != 0 {
 			foundIwPtr = true
-			meAttribute := fmt.Sprintf("InterworkTpPointerForPBitPriority%d", index)
 			meParams.Attributes[meAttribute] = value
 			logger.Debugw("UniPonAniConfigFsm Set::1pMapper", log.Fields{
 				"for Prio":  index,
 				"IwPtr":     strconv.FormatInt(int64(value), 16),
 				"device-id": oFsm.deviceID})
+		} else {
+			// The null pointer 0xFFFF specifies that frames with the associated priority are to be discarded.
+			meParams.Attributes[meAttribute] = 0xffff
 		}
 	}
+	// The TP type value 0 also indicates bridging mapping, and the TP pointer should be set to 0xFFFF
+	meParams.Attributes["TpPointer"] = 0xffff
 
 	if !foundIwPtr {
-		logger.Errorw("UniPonAniConfigFsm no GemIwPtr found for .1pMapper - abort", log.Fields{
+		logger.Debugw("UniPonAniConfigFsm no GemIwPtr found for .1pMapper - abort", log.Fields{
 			"device-id": oFsm.deviceID})
+		//TODO With multicast is possible that no upstream gem ports are not present in the tech profile,
+		// this reset needs to be performed only if the tech profile provides upstream gem ports but no priority is set
 		//let's reset the state machine in order to release all resources now
+		//pConfigAniStateAFsm := oFsm.pAdaptFsm
+		//if pConfigAniStateAFsm != nil {
+		//	// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+		//	go func(aPAFsm *AdapterFsm) {
+		//		if aPAFsm != nil && aPAFsm.pFsm != nil {
+		//			_ = aPAFsm.pFsm.Event(aniEvReset)
+		//		}
+		//	}(pConfigAniStateAFsm)
+		//}
+		//Moving forward the FSM as if the response was received correctly.
 		pConfigAniStateAFsm := oFsm.pAdaptFsm
 		if pConfigAniStateAFsm != nil {
 			// obviously calling some FSM event here directly does not work - so trying to decouple it ...
 			go func(aPAFsm *AdapterFsm) {
 				if aPAFsm != nil && aPAFsm.pFsm != nil {
-					_ = aPAFsm.pFsm.Event(aniEvReset)
+					_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxDot1pmapSResp)
 				}
 			}(pConfigAniStateAFsm)
 		}
+	} else {
+		meInstance := oFsm.pOmciCC.sendSetDot1PMapperVar(context.TODO(), ConstDefaultOmciTimeout, true,
+			oFsm.pAdaptFsm.commChan, meParams)
+		//accept also nil as (error) return value for writing to LastTx
+		//  - this avoids misinterpretation of new received OMCI messages
+		oFsm.pLastTxMeInstance = meInstance
 	}
 
-	meInstance := oFsm.pOmciCC.sendSetDot1PMapperVar(context.TODO(), ConstDefaultOmciTimeout, true,
-		oFsm.pAdaptFsm.commChan, meParams)
-	//accept also nil as (error) return value for writing to LastTx
-	//  - this avoids misinterpretation of new received OMCI messages
-	oFsm.pLastTxMeInstance = meInstance
 }
 
 func (oFsm *uniPonAniConfigFsm) enterAniConfigDone(e *fsm.Event) {
@@ -775,7 +814,7 @@
 				{ // let the FSM proceed ...
 					_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxMbpcdResp)
 				}
-			case "GemPortNetworkCtp", "GemInterworkingTerminationPoint":
+			case "GemPortNetworkCtp", "GemInterworkingTerminationPoint", "MulticastGemInterworkingTerminationPoint":
 				{ // let aniConfig Multi-Id processing proceed by stopping the wait function
 					oFsm.omciMIdsResponseReceived <- true
 				}
@@ -823,7 +862,7 @@
 					_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxResetTcontResp)
 				}
 			}
-		case "PriorityQueue":
+		case "PriorityQueue", "MulticastGemInterworkingTerminationPoint":
 			{ // let the PrioQueue init proceed by stopping the wait function
 				oFsm.omciMIdsResponseReceived <- true
 			}
@@ -961,27 +1000,46 @@
 
 		//TODO if the port has only downstream direction the isMulticast flag can be removed.
 		if gemPortAttribs.isMulticast {
-			ipv4MulticastTable := make([]uint8, 12)
-			binary.BigEndian.PutUint16(ipv4MulticastTable[0:], gemPortAttribs.gemPortID)
-			binary.BigEndian.PutUint16(ipv4MulticastTable[2:], 0)
-			// This is the 224.0.0.1 address
-			binary.BigEndian.PutUint32(ipv4MulticastTable[4:], IPToInt32(net.IPv4allsys))
-			// this is the 255.255.255.255 address
-			binary.BigEndian.PutUint32(ipv4MulticastTable[8:], IPToInt32(net.IPv4bcast))
 
 			meParams := me.ParamData{
-				EntityID: gemPortAttribs.gemPortID,
+				EntityID: gemPortAttribs.multicastGemID,
 				Attributes: me.AttributeValueMap{
-					"GemPortNetworkCtpConnectivityPointer": gemPortAttribs.gemPortID,
+					"GemPortNetworkCtpConnectivityPointer": gemPortAttribs.multicastGemID,
 					"InterworkingOption":                   0, // Don't Care
 					"ServiceProfilePointer":                0, // Don't Care
 					"GalProfilePointer":                    galEthernetEID,
-					"Ipv4MulticastAddressTable":            ipv4MulticastTable,
 				},
 			}
 			meInstance := oFsm.pOmciCC.sendCreateMulticastGemIWTPVar(context.TODO(), ConstDefaultOmciTimeout,
 				true, oFsm.pAdaptFsm.commChan, meParams)
 			oFsm.pLastTxMeInstance = meInstance
+			//verify response
+			err := oFsm.waitforOmciResponse()
+			if err != nil {
+				logger.Errorw("GemTP IW multicast create failed, aborting AniConfig FSM!",
+					log.Fields{"device-id": oFsm.deviceID, "GemIndex": gemIndex})
+				_ = oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
+				return
+			}
+			ipv4MulticastTable := make([]uint8, 12)
+			//Gem Port ID
+			binary.BigEndian.PutUint16(ipv4MulticastTable[0:], gemPortAttribs.multicastGemID)
+			//Secondary Key
+			binary.BigEndian.PutUint16(ipv4MulticastTable[2:], 0)
+			// Multicast IP range start This is the 224.0.0.1 address
+			binary.BigEndian.PutUint32(ipv4MulticastTable[4:], IPToInt32(net.IPv4(224, 0, 0, 0)))
+			// MulticastIp range stop
+			binary.BigEndian.PutUint32(ipv4MulticastTable[8:], IPToInt32(net.IPv4(239, 255, 255, 255)))
+
+			meIPV4MCTableParams := me.ParamData{
+				EntityID: gemPortAttribs.multicastGemID,
+				Attributes: me.AttributeValueMap{
+					"Ipv4MulticastAddressTable": ipv4MulticastTable,
+				},
+			}
+			meIPV4MCTableInstance := oFsm.pOmciCC.sendSetMulticastGemIWTPVar(context.TODO(), ConstDefaultOmciTimeout,
+				true, oFsm.pAdaptFsm.commChan, meIPV4MCTableParams)
+			oFsm.pLastTxMeInstance = meIPV4MCTableInstance
 
 		} else {
 			meParams := me.ParamData{
@@ -1020,6 +1078,12 @@
 	//find all upstream PrioQueues related to this T-Cont
 	loQueueMap := ordered_map.NewOrderedMap()
 	for _, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+		if gemPortAttribs.isMulticast {
+			logger.Debugw("uniPonAniConfigFsm Port is Multicast, ignoring PQs", log.Fields{
+				"device-id": oFsm.deviceID, "GemPort": gemPortAttribs.gemPortID,
+				"prioString": gemPortAttribs.pbitString})
+			continue
+		}
 		if gemPortAttribs.qosPolicy == "WRR" {
 			if _, ok := loQueueMap.Get(gemPortAttribs.upQueueID); !ok {
 				//key does not yet exist
diff --git a/internal/pkg/onuadaptercore/omci_cc.go b/internal/pkg/onuadaptercore/omci_cc.go
index 17e674d..deb0905 100644
--- a/internal/pkg/onuadaptercore/omci_cc.go
+++ b/internal/pkg/onuadaptercore/omci_cc.go
@@ -804,13 +804,11 @@
 	meParams := me.ParamData{
 		EntityID: instID,
 		Attributes: me.AttributeValueMap{
-			"Priority":     0x8000,
-			"MaxAge":       20 * 256, //20s
-			"HelloTime":    2 * 256,  //2s
-			"ForwardDelay": 15 * 256, //15s
-			//note: DynamicFilteringAgeingTime is taken from omci lib default as
-			//  which is obviously different from default value used in python lib,
-			//  where the value seems to be 0 (ONU defined)  - to be considered in case of test artifacts ...
+			"Priority":                   0x8000,
+			"MaxAge":                     20 * 256, //20s
+			"HelloTime":                  2 * 256,  //2s
+			"ForwardDelay":               15 * 256, //15s
+			"DynamicFilteringAgeingTime": 0,
 		},
 	}
 
@@ -1526,6 +1524,7 @@
 	return nil
 }
 
+// nolint: unused
 func (oo *omciCC) sendSetVtfdVar(ctx context.Context, timeout int, highPrio bool,
 	rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
 	tid := oo.getNextTid(highPrio)
@@ -1571,6 +1570,47 @@
 	return nil
 }
 
+func (oo *omciCC) sendCreateEvtocdVar(ctx context.Context, timeout int, highPrio bool,
+	rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw("send EVTOCD-Create-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16),
+		"InstId": strconv.FormatInt(int64(params[0].EntityID), 16)})
+
+	meInstance, omciErr := me.NewExtendedVlanTaggingOperationConfigurationData(params[0])
+	if omciErr.GetError() == nil {
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType, omci.TransactionID(tid))
+		if err != nil {
+			logger.Errorw("Cannot encode EVTOCD for create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize EVTOCD create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send EVTOCD create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send EVTOCD-set msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate EVTOCD Instance", log.Fields{
+		"Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
+
 func (oo *omciCC) sendSetEvtocdVar(ctx context.Context, timeout int, highPrio bool,
 	rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
 	tid := oo.getNextTid(highPrio)
@@ -1612,6 +1652,47 @@
 	return nil
 }
 
+func (oo *omciCC) sendDeleteEvtocd(ctx context.Context, timeout int, highPrio bool,
+	rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw("send EVTOCD-Delete-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16),
+		"InstId": strconv.FormatInt(int64(params[0].EntityID), 16)})
+
+	meInstance, omciErr := me.NewExtendedVlanTaggingOperationConfigurationData(params[0])
+	if omciErr.GetError() == nil {
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.DeleteRequestType, omci.TransactionID(tid))
+		if err != nil {
+			logger.Errorw("Cannot encode EVTOCD for delete", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize EVTOCD delete", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send EVTOCD delete", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send EVTOCD-delete msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate EVTOCD Instance", log.Fields{
+		"Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
+
 func (oo *omciCC) sendDeleteVtfd(ctx context.Context, timeout int, highPrio bool,
 	rxChan chan Message, aInstID uint16) *me.ManagedEntity {
 	tid := oo.getNextTid(highPrio)
@@ -1882,6 +1963,45 @@
 }
 
 // nolint: unused
+func (oo *omciCC) sendSetMulticastGemIWTPVar(ctx context.Context, timeout int, highPrio bool,
+	rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw("send MulticastGemIWTP-set-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16),
+		"InstId": strconv.FormatInt(int64(params[0].EntityID), 16)})
+
+	meInstance, omciErr := me.NewMulticastGemInterworkingTerminationPoint(params[0])
+	if omciErr.GetError() == nil {
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.SetRequestType, omci.TransactionID(tid),
+			omci.AddDefaults(true))
+		if err != nil {
+			logger.Errorw("Cannot encode MulticastGEMIWTP for set", log.Fields{"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize MulticastGEMIWTP create", log.Fields{"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{cbKey: tid,
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send MulticastGEMIWTP set", log.Fields{"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send MulticastGEMIWTP-set-msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate MulticastGEMIWTP Instance", log.Fields{"Err": omciErr.GetError(),
+		"device-id": oo.deviceID})
+	return nil
+}
+
+// nolint: unused
 func (oo *omciCC) sendCreateMulticastOperationProfileVar(ctx context.Context, timeout int, highPrio bool,
 	rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
 	tid := oo.getNextTid(highPrio)
diff --git a/internal/pkg/onuadaptercore/omci_vlan_config.go b/internal/pkg/onuadaptercore/omci_vlan_config.go
index aeb6967..b9c9d79 100644
--- a/internal/pkg/onuadaptercore/omci_vlan_config.go
+++ b/internal/pkg/onuadaptercore/omci_vlan_config.go
@@ -21,6 +21,7 @@
 	"context"
 	"encoding/binary"
 	"fmt"
+	"net"
 	"strconv"
 	"sync"
 	"time"
@@ -135,7 +136,6 @@
 	pOnuUniPort                 *onuUniPort
 	pUniTechProf                *onuUniTechProf
 	pOnuDB                      *onuDeviceDB
-	techProfileID               uint16
 	requestEvent                OnuDeviceEvent
 	omciMIdsResponseReceived    chan bool //seperate channel needed for checking multiInstance OMCI message responses
 	pAdaptFsm                   *AdapterFsm
@@ -149,7 +149,6 @@
 	numRemoveFlows              uint8
 	numVlanFilterEntries        uint8
 	vlanFilterList              [cVtfdTableSize]uint16
-	vtfdID                      uint16
 	evtocdID                    uint16
 	pLastTxMeInstance           *me.ManagedEntity
 	requestEventOffset          uint8
@@ -168,7 +167,6 @@
 		pOnuUniPort:                 apUniPort,
 		pUniTechProf:                apUniTechProf,
 		pOnuDB:                      apOnuDB,
-		techProfileID:               aTechProfileID,
 		requestEvent:                aRequestEvent,
 		acceptIncrementalEvtoOption: aAcceptIncrementalEvto,
 		numUniFlows:                 0,
@@ -482,7 +480,7 @@
 						oFsm.uniVlanFlowParamsSlice = nil //reset the slice
 						//at this point it is evident that no flow anymore refers to a still possibly active Techprofile
 						//request that this profile gets deleted before a new flow add is allowed
-						oFsm.pUniTechProf.setProfileToDelete(oFsm.pOnuUniPort.uniID, uint8(oFsm.techProfileID), true)
+						oFsm.pUniTechProf.setProfileToDelete(oFsm.pOnuUniPort.uniID, uint8(loRemoveParams.vlanRuleParams.TpID), true)
 						logger.Debugw("UniVlanConfigFsm flow removal - no more flows", log.Fields{
 							"device-id": oFsm.deviceID})
 					} else {
@@ -512,7 +510,7 @@
 							logger.Debugw("UniVlanConfigFsm tp-id used in deleted flow is not used anymore", log.Fields{
 								"device-id": oFsm.deviceID, "tp-id": usedTpID})
 							//request that this profile gets deleted before a new flow add is allowed
-							oFsm.pUniTechProf.setProfileToDelete(oFsm.pOnuUniPort.uniID, uint8(oFsm.techProfileID), true)
+							oFsm.pUniTechProf.setProfileToDelete(oFsm.pOnuUniPort.uniID, uint8(usedTpID), true)
 						}
 						logger.Debugw("UniVlanConfigFsm flow removal - specific flow removed from data", log.Fields{
 							"device-id": oFsm.deviceID})
@@ -588,12 +586,11 @@
 		// obviously calling some FSM event here directly does not work - so trying to decouple it ...
 		go func(a_pAFsm *AdapterFsm) {
 			if a_pAFsm != nil && a_pAFsm.pFsm != nil {
-				//stick to pythonAdapter numbering scheme
-				oFsm.vtfdID = macBridgePortAniEID + oFsm.pOnuUniPort.entityID + oFsm.techProfileID
+				tpID := oFsm.uniVlanFlowParamsSlice[oFsm.configuredUniFlow].VlanRuleParams.TpID
 				//cmp also usage in EVTOCDE create in omci_cc
 				oFsm.evtocdID = macBridgeServiceProfileEID + uint16(oFsm.pOnuUniPort.macBpNo)
 
-				if oFsm.pUniTechProf.getTechProfileDone(oFsm.pOnuUniPort.uniID, uint8(oFsm.techProfileID)) {
+				if oFsm.pUniTechProf.getTechProfileDone(oFsm.pOnuUniPort.uniID, uint8(tpID)) {
 					// let the vlan processing begin
 					_ = a_pAFsm.pFsm.Event(vlanEvStartConfig)
 				} else {
@@ -630,8 +627,11 @@
 			_ = a_pAFsm.pFsm.Event(vlanEvRxConfigVtfd)
 		}(pConfigVlanStateAFsm)
 	} else {
+		// This attribute uniquely identifies each instance of this managed entity. Through an identical ID,
+		// this managed entity is implicitly linked to an instance of the MAC bridge port configuration data ME.
+		vtfdID := macBridgePortAniEID + oFsm.pOnuUniPort.entityID + oFsm.uniVlanFlowParamsSlice[0].VlanRuleParams.TpID
 		logger.Debugw("UniVlanConfigFsm create VTFD", log.Fields{
-			"EntitytId": strconv.FormatInt(int64(oFsm.vtfdID), 16),
+			"EntitytId": strconv.FormatInt(int64(vtfdID), 16),
 			"in state":  e.FSM.Current(), "device-id": oFsm.deviceID})
 		// setVid is assumed to be masked already by the caller to 12 bit
 		oFsm.vlanFilterList[0] = uint16(oFsm.uniVlanFlowParamsSlice[0].VlanRuleParams.SetVid)
@@ -640,7 +640,7 @@
 		vtfdFilterList[0] = oFsm.vlanFilterList[0]
 		oFsm.numVlanFilterEntries = 1
 		meParams := me.ParamData{
-			EntityID: oFsm.vtfdID,
+			EntityID: vtfdID,
 			Attributes: me.AttributeValueMap{
 				"VlanFilterList":   vtfdFilterList, //omci lib wants a slice for serialization
 				"ForwardOperation": uint8(0x10),    //VID investigation
@@ -664,7 +664,28 @@
 	logger.Debugw("UniVlanConfigFsm - start config EVTOCD loop", log.Fields{
 		"in state": e.FSM.Current(), "device-id": oFsm.deviceID})
 	oFsm.requestEventOffset = 0 //0 offset for last flow-add activity
-	go oFsm.performConfigEvtocdEntries(0)
+	go func() {
+		tpID := oFsm.uniVlanFlowParamsSlice[0].VlanRuleParams.TpID
+		vlanID := oFsm.uniVlanFlowParamsSlice[0].VlanRuleParams.SetVid
+		errEvto := oFsm.performConfigEvtocdEntries(oFsm.configuredUniFlow)
+		//This is correct passing scenario
+		if errEvto == nil {
+			for _, gemPort := range oFsm.pUniTechProf.getMulticastGemPorts(oFsm.pOnuUniPort.uniID, uint8(tpID)) {
+				log.Infow("Setting multicast MEs, with first flow", log.Fields{"deviceID": oFsm.deviceID,
+					"techProfile": tpID, "gemPort": gemPort, "vlanID": vlanID, "configuredUniFlow": oFsm.configuredUniFlow})
+				//can Use the first elements in the slice because it's the first flow.
+				errCreateAllMulticastME := oFsm.performSettingMulticastME(tpID, gemPort,
+					vlanID)
+				if errCreateAllMulticastME != nil {
+					logger.Errorw("Multicast ME create failed, aborting AniConfig FSM!",
+						log.Fields{"device-id": oFsm.deviceID})
+					_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
+				}
+			}
+			//TODO Possibly insert new state for multicast --> possibly another jira/later time.
+			_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvRxConfigEvtocd)
+		}
+	}()
 }
 
 func (oFsm *UniVlanConfigFsm) enterVlanConfigDone(e *fsm.Event) {
@@ -732,18 +753,25 @@
 			"in state": e.FSM.Current(), "device-id": oFsm.deviceID})
 	} else {
 		if oFsm.numVlanFilterEntries == 0 {
+			// This attribute uniquely identifies each instance of this managed entity. Through an identical ID,
+			// this managed entity is implicitly linked to an instance of the MAC bridge port configuration data ME.
+			vtfdID := macBridgePortAniEID + oFsm.pOnuUniPort.entityID + oFsm.uniVlanFlowParamsSlice[0].VlanRuleParams.TpID
 			//no VTFD yet created
 			logger.Debugw("UniVlanConfigFsm create VTFD", log.Fields{
-				"EntitytId": strconv.FormatInt(int64(oFsm.vtfdID), 16),
+				"EntitytId": strconv.FormatInt(int64(vtfdID), 16),
 				"in state":  e.FSM.Current(), "device-id": oFsm.deviceID})
-			// setVid is assumed to be masked already by the caller to 12 bit
-			oFsm.vlanFilterList[0] = uint16(oFsm.uniVlanFlowParamsSlice[oFsm.configuredUniFlow].VlanRuleParams.SetVid)
+			// FIXME: VOL-3673: using oFsm.uniVlanFlowParamsSlice[0] is incorrect here, as the relevant (first) VTFD may
+			// result from some incremented rule (not all rules enforce a VTFD configuration). But this is Ok for the
+			// current scenarios we support.
+			// 'SetVid' below is assumed to be masked already by the caller to 12 bit
+			oFsm.vlanFilterList[0] = uint16(oFsm.uniVlanFlowParamsSlice[0].VlanRuleParams.SetVid)
+
 			oFsm.mutexFlowParams.Unlock()
 			vtfdFilterList := make([]uint16, cVtfdTableSize) //needed for parameter serialization
 			vtfdFilterList[0] = oFsm.vlanFilterList[0]
 			oFsm.numVlanFilterEntries = 1
 			meParams := me.ParamData{
-				EntityID: oFsm.vtfdID,
+				EntityID: vtfdID,
 				Attributes: me.AttributeValueMap{
 					"VlanFilterList":   vtfdFilterList,
 					"ForwardOperation": uint8(0x10), //VID investigation
@@ -759,29 +787,34 @@
 			//  (relevant to all used sendXX() methods in this (and other) FSM's)
 			oFsm.pLastTxMeInstance = meInstance
 		} else {
-			//VTFD already exists - just modify by 'set'
-			//TODO!!: but only if the VID is not already present, skipped by now to test basic working
+			// This attribute uniquely identifies each instance of this managed entity. Through an identical ID,
+			// this managed entity is implicitly linked to an instance of the MAC bridge port configuration data ME.
+			vtfdID := macBridgePortAniEID + oFsm.pOnuUniPort.entityID + oFsm.uniVlanFlowParamsSlice[oFsm.configuredUniFlow].VlanRuleParams.TpID
+
 			logger.Debugw("UniVlanConfigFsm set VTFD", log.Fields{
-				"EntitytId": strconv.FormatInt(int64(oFsm.vtfdID), 16),
+				"EntitytId": strconv.FormatInt(int64(vtfdID), 16),
 				"in state":  e.FSM.Current(), "device-id": oFsm.deviceID})
 			// setVid is assumed to be masked already by the caller to 12 bit
 			oFsm.vlanFilterList[oFsm.numVlanFilterEntries] =
 				uint16(oFsm.uniVlanFlowParamsSlice[oFsm.configuredUniFlow].VlanRuleParams.SetVid)
-			oFsm.mutexFlowParams.Unlock()
 			vtfdFilterList := make([]uint16, cVtfdTableSize) //needed for parameter serialization
-			for i := uint8(0); i <= oFsm.numVlanFilterEntries; i++ {
-				vtfdFilterList[i] = oFsm.vlanFilterList[i]
-			}
+
+			// FIXME: VOL-3685: Issues with resetting a table entry in EVTOCD ME
+			// VTFD has to be created afresh with a new entity ID that has the same entity ID as the MBPCD ME for every
+			// new vlan associated with a different TP.
+			vtfdFilterList[0] = uint16(oFsm.uniVlanFlowParamsSlice[oFsm.configuredUniFlow].VlanRuleParams.SetVid)
+			oFsm.mutexFlowParams.Unlock()
 
 			oFsm.numVlanFilterEntries++
 			meParams := me.ParamData{
-				EntityID: oFsm.vtfdID,
+				EntityID: vtfdID,
 				Attributes: me.AttributeValueMap{
-					"VlanFilterList":  vtfdFilterList,
-					"NumberOfEntries": oFsm.numVlanFilterEntries,
+					"VlanFilterList":   vtfdFilterList,
+					"ForwardOperation": uint8(0x10), //VID investigation
+					"NumberOfEntries":  oFsm.numVlanFilterEntries,
 				},
 			}
-			meInstance := oFsm.pOmciCC.sendSetVtfdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+			meInstance := oFsm.pOmciCC.sendCreateVtfdVar(context.TODO(), ConstDefaultOmciTimeout, true,
 				oFsm.pAdaptFsm.commChan, meParams)
 			//accept also nil as (error) return value for writing to LastTx
 			//  - this avoids misinterpretation of new received OMCI messages
@@ -803,7 +836,28 @@
 		}
 	}
 	oFsm.requestEventOffset = 0 //0 offset for last flow-add activity
-	go oFsm.performConfigEvtocdEntries(oFsm.configuredUniFlow)
+	go func() {
+		tpID := oFsm.uniVlanFlowParamsSlice[oFsm.configuredUniFlow].VlanRuleParams.TpID
+		errEvto := oFsm.performConfigEvtocdEntries(oFsm.configuredUniFlow)
+		//This is correct passing scenario
+		if errEvto == nil {
+			//TODO Possibly insert new state for multicast --> possibly another jira/later time.
+			for _, gemPort := range oFsm.pUniTechProf.getMulticastGemPorts(oFsm.pOnuUniPort.uniID, uint8(tpID)) {
+				vlanID := oFsm.uniVlanFlowParamsSlice[oFsm.configuredUniFlow-1].VlanRuleParams.SetVid
+				log.Infow("Setting multicast MEs for additional flows", log.Fields{"deviceID": oFsm.deviceID,
+					"techProfile": tpID, "gemPort": gemPort,
+					"vlanID": vlanID, "configuredUniFlow": oFsm.configuredUniFlow})
+				//-1 is to use the last configured flow
+				errCreateAllMulticastME := oFsm.performSettingMulticastME(tpID, gemPort, vlanID)
+				if errCreateAllMulticastME != nil {
+					logger.Errorw("Multicast ME create failed, aborting AniConfig FSM!",
+						log.Fields{"device-id": oFsm.deviceID})
+					_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
+				}
+			}
+			_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvRxConfigEvtocd)
+		}
+	}()
 }
 
 func (oFsm *UniVlanConfigFsm) enterRemoveFlow(e *fsm.Event) {
@@ -831,6 +885,7 @@
 	} else {
 		vtfdFilterList := make([]uint16, cVtfdTableSize) //needed for parameter serialization and 're-copy'
 		if oFsm.numVlanFilterEntries == 1 {
+			vtfdID := macBridgePortAniEID + oFsm.pOnuUniPort.entityID + loRuleParams.TpID
 			//only one active VLAN entry (hopefully the SetVID we want to remove - should be, but not verified ..)
 			//  so we can just delete the VTFD entry
 			logger.Debugw("UniVlanConfigFsm: VTFD delete (no more vlan filters)",
@@ -839,7 +894,7 @@
 			loVlanEntryClear = 1           //full VlanFilter clear request
 			if loAllowSpecificOmciConfig { //specific OMCI config is expected to work acc. to the device state
 				meInstance := oFsm.pOmciCC.sendDeleteVtfd(context.TODO(), ConstDefaultOmciTimeout, true,
-					oFsm.pAdaptFsm.commChan, oFsm.vtfdID)
+					oFsm.pAdaptFsm.commChan, vtfdID)
 				oFsm.pLastTxMeInstance = meInstance
 			} else {
 				logger.Debugw("UniVlanConfigFsm delete VTFD OMCI handling skipped based on device state", log.Fields{
@@ -858,6 +913,7 @@
 				}
 			}
 			if loVlanEntryRmPos < cVtfdTableSize {
+				vtfdID := macBridgePortAniEID + oFsm.pOnuUniPort.entityID + loRuleParams.TpID
 				//valid entry was found - to be eclipsed
 				loVlanEntryClear = 2 //VlanFilter remove request for a specific entry
 				for i := uint8(0); i < oFsm.numVlanFilterEntries; i++ {
@@ -870,19 +926,13 @@
 					}
 				}
 				logger.Debugw("UniVlanConfigFsm set VTFD", log.Fields{
-					"EntitytId":     strconv.FormatInt(int64(oFsm.vtfdID), 16),
+					"EntitytId":     strconv.FormatInt(int64(vtfdID), 16),
 					"new vlan list": vtfdFilterList, "device-id": oFsm.deviceID})
 
 				if loAllowSpecificOmciConfig { //specific OMCI config is expected to work acc. to the device state
-					meParams := me.ParamData{
-						EntityID: oFsm.vtfdID,
-						Attributes: me.AttributeValueMap{
-							"VlanFilterList":  vtfdFilterList,
-							"NumberOfEntries": oFsm.numVlanFilterEntries - 1, //one element less
-						},
-					}
-					meInstance := oFsm.pOmciCC.sendSetVtfdVar(context.TODO(), ConstDefaultOmciTimeout, true,
-						oFsm.pAdaptFsm.commChan, meParams)
+					// FIXME: VOL-3685: Issues with resetting a table entry in EVTOCD ME
+					meInstance := oFsm.pOmciCC.sendDeleteVtfd(context.TODO(), ConstDefaultOmciTimeout, true,
+						oFsm.pAdaptFsm.commChan, vtfdID)
 					oFsm.pLastTxMeInstance = meInstance
 				} else {
 					logger.Debugw("UniVlanConfigFsm set VTFD OMCI handling skipped based on device state", log.Fields{
@@ -1083,8 +1133,7 @@
 			if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
 				msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
 				switch oFsm.pLastTxMeInstance.GetName() {
-				case "VlanTaggingFilterData",
-					"ExtendedVlanTaggingOperationConfigurationData":
+				case "VlanTaggingFilterData", "ExtendedVlanTaggingOperationConfigurationData", "MulticastOperationsProfile":
 					{ // let the MultiEntity config proceed by stopping the wait function
 						oFsm.omciMIdsResponseReceived <- true
 					}
@@ -1123,7 +1172,7 @@
 			oFsm.deviceID)
 	}
 	logger.Debugw("UniVlanConfigFsm CreateResponse Data", log.Fields{"device-id": oFsm.deviceID, "data-fields": msgObj})
-	if msgObj.Result != me.Success {
+	if msgObj.Result != me.Success && msgObj.Result != me.InstanceExists {
 		logger.Errorw("Omci CreateResponse Error - later: drive FSM to abort state ?", log.Fields{"device-id": oFsm.deviceID,
 			"Error": msgObj.Result})
 		// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
@@ -1134,7 +1183,9 @@
 		msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
 		// to satisfy StaticCodeAnalysis I had to move the small processing into a separate method :-(
 		switch oFsm.pLastTxMeInstance.GetName() {
-		case "VlanTaggingFilterData":
+		case "VlanTaggingFilterData", "MulticastOperationsProfile",
+			"MulticastSubscriberConfigInfo", "MacBridgePortConfigurationData",
+			"ExtendedVlanTaggingOperationConfigurationData":
 			{
 				if oFsm.pAdaptFsm.pFsm.Current() == vlanStConfigVtfd {
 					// Only if CreateResponse is received from first flow entry - let the FSM proceed ...
@@ -1174,7 +1225,7 @@
 	if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
 		msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
 		switch oFsm.pLastTxMeInstance.GetName() {
-		case "VlanTaggingFilterData":
+		case "VlanTaggingFilterData", "ExtendedVlanTaggingOperationConfigurationData":
 			{ // let the MultiEntity config proceed by stopping the wait function
 				oFsm.omciMIdsResponseReceived <- true
 			}
@@ -1183,24 +1234,31 @@
 	return nil
 }
 
-func (oFsm *UniVlanConfigFsm) performConfigEvtocdEntries(aFlowEntryNo uint8) {
+func (oFsm *UniVlanConfigFsm) performConfigEvtocdEntries(aFlowEntryNo uint8) error {
 	if aFlowEntryNo == 0 {
 		// EthType set only at first flow element
 		// EVTOCD ME is expected to exist at this point already from MIB-Download (with AssociationType/Pointer)
 		// we need to extend the configuration by EthType definition and, to be sure, downstream 'inverse' mode
-		logger.Debugw("UniVlanConfigFsm Tx Set::EVTOCD", log.Fields{
+		logger.Debugw("UniVlanConfigFsm Tx Create::EVTOCD", log.Fields{
 			"EntitytId":  strconv.FormatInt(int64(oFsm.evtocdID), 16),
 			"i/oEthType": strconv.FormatInt(int64(cDefaultTpid), 16),
 			"device-id":  oFsm.deviceID})
+		associationType := 2 // default to uniPPTP
+		if oFsm.pOnuUniPort.portType == uniVEIP {
+			associationType = 10
+		}
+		// Create the EVTOCD ME
 		meParams := me.ParamData{
 			EntityID: oFsm.evtocdID,
 			Attributes: me.AttributeValueMap{
-				"InputTpid":      uint16(cDefaultTpid), //could be possibly retrieved from flow config one day, by now just like py-code base
-				"OutputTpid":     uint16(cDefaultTpid), //could be possibly retrieved from flow config one day, by now just like py-code base
-				"DownstreamMode": uint8(cDefaultDownstreamMode),
+				"InputTpid":           uint16(cDefaultTpid), //could be possibly retrieved from flow config one day, by now just like py-code base
+				"OutputTpid":          uint16(cDefaultTpid), //could be possibly retrieved from flow config one day, by now just like py-code base
+				"DownstreamMode":      uint8(cDefaultDownstreamMode),
+				"AssociationType":     uint8(associationType),
+				"AssociatedMePointer": oFsm.pOnuUniPort.entityID,
 			},
 		}
-		meInstance := oFsm.pOmciCC.sendSetEvtocdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+		meInstance := oFsm.pOmciCC.sendCreateEvtocdVar(context.TODO(), ConstDefaultOmciTimeout, true,
 			oFsm.pAdaptFsm.commChan, meParams)
 		//accept also nil as (error) return value for writing to LastTx
 		//  - this avoids misinterpretation of new received OMCI messages
@@ -1209,10 +1267,34 @@
 		//verify response
 		err := oFsm.waitforOmciResponse()
 		if err != nil {
+			logger.Errorw("Evtocd create failed, aborting VlanConfig FSM!",
+				log.Fields{"device-id": oFsm.deviceID})
+			_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
+			return fmt.Errorf("evtocd create failed %s, error %s", oFsm.deviceID, err)
+		}
+
+		// Set the EVTOCD ME default params
+		meParams = me.ParamData{
+			EntityID: oFsm.evtocdID,
+			Attributes: me.AttributeValueMap{
+				"InputTpid":      uint16(cDefaultTpid), //could be possibly retrieved from flow config one day, by now just like py-code base
+				"OutputTpid":     uint16(cDefaultTpid), //could be possibly retrieved from flow config one day, by now just like py-code base
+				"DownstreamMode": uint8(cDefaultDownstreamMode),
+			},
+		}
+		meInstance = oFsm.pOmciCC.sendSetEvtocdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+			oFsm.pAdaptFsm.commChan, meParams)
+		//accept also nil as (error) return value for writing to LastTx
+		//  - this avoids misinterpretation of new received OMCI messages
+		oFsm.pLastTxMeInstance = meInstance
+
+		//verify response
+		err = oFsm.waitforOmciResponse()
+		if err != nil {
 			logger.Errorw("Evtocd set TPID failed, aborting VlanConfig FSM!",
 				log.Fields{"device-id": oFsm.deviceID})
 			_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
-			return
+			return fmt.Errorf("evtocd set TPID failed %s, error %s", oFsm.deviceID, err)
 		}
 	} //first flow element
 
@@ -1225,7 +1307,7 @@
 		go func(a_pAFsm *AdapterFsm) {
 			_ = a_pAFsm.pFsm.Event(vlanEvReset)
 		}(pConfigVlanStateAFsm)
-		return
+		return nil
 	}
 
 	if oFsm.uniVlanFlowParamsSlice[aFlowEntryNo].VlanRuleParams.SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
@@ -1275,14 +1357,19 @@
 			logger.Errorw("Evtocd set transparent singletagged rule failed, aborting VlanConfig FSM!",
 				log.Fields{"device-id": oFsm.deviceID})
 			_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
-			return
+			return fmt.Errorf("evtocd set transparent singletagged rule failed %s, error %s", oFsm.deviceID, err)
+
 		}
 	} else {
 		// according to py-code acceptIncrementalEvto program option decides upon stacking or translation scenario
 		if oFsm.acceptIncrementalEvtoOption {
+			matchPcp := oFsm.uniVlanFlowParamsSlice[aFlowEntryNo].VlanRuleParams.MatchPcp
+			matchVid := oFsm.uniVlanFlowParamsSlice[aFlowEntryNo].VlanRuleParams.MatchVid
+			setPcp := oFsm.uniVlanFlowParamsSlice[aFlowEntryNo].VlanRuleParams.SetPcp
+			setVid := oFsm.uniVlanFlowParamsSlice[aFlowEntryNo].VlanRuleParams.SetVid
 			// this defines VID translation scenario: singletagged->singletagged (if not transparent)
 			logger.Debugw("UniVlanConfigFsm Tx Set::EVTOCD single tagged translation rule", log.Fields{
-				"device-id": oFsm.deviceID})
+				"match-pcp": matchPcp, "match-vid": matchVid, "set-pcp": setPcp, "set-vid:": setVid, "device-id": oFsm.deviceID})
 			sliceEvtocdRule := make([]uint8, 16)
 			// fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
 			binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
@@ -1326,7 +1413,7 @@
 				logger.Errorw("Evtocd set singletagged translation rule failed, aborting VlanConfig FSM!",
 					log.Fields{"device-id": oFsm.deviceID})
 				_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
-				return
+				return fmt.Errorf("evtocd set singletagged translation rule failed %s, error %s", oFsm.deviceID, err)
 			}
 		} else {
 			//not transparent and not acceptIncrementalEvtoOption untagged/priotagged->singletagged
@@ -1378,7 +1465,8 @@
 					logger.Errorw("Evtocd set untagged->singletagged rule failed, aborting VlanConfig FSM!",
 						log.Fields{"device-id": oFsm.deviceID})
 					_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
-					return
+					return fmt.Errorf("evtocd set untagged->singletagged rule failed %s, error %s", oFsm.deviceID, err)
+
 				}
 			} // just for local var's
 			{ // just for local var's
@@ -1430,7 +1518,8 @@
 					logger.Errorw("Evtocd set priotagged->singletagged rule failed, aborting VlanConfig FSM!",
 						log.Fields{"device-id": oFsm.deviceID})
 					_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
-					return
+					return fmt.Errorf("evtocd set priotagged->singletagged rule failed %s, error %s", oFsm.deviceID, err)
+
 				}
 			} //just for local var's
 		}
@@ -1439,7 +1528,7 @@
 	// if Config has been done for all EVTOCD entries let the FSM proceed
 	logger.Debugw("EVTOCD set loop finished", log.Fields{"device-id": oFsm.deviceID})
 	oFsm.configuredUniFlow++ // one (more) flow configured
-	_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvRxConfigEvtocd)
+	return nil
 }
 
 func (oFsm *UniVlanConfigFsm) removeEvtocdEntries(aRuleParams uniVlanRuleParams) {
@@ -1538,46 +1627,24 @@
 				return
 			}
 		} else {
-			//not transparent and not acceptIncrementalEvtoOption: untagged/priotagged->singletagged
-			{ // just for local var's
-				// this defines stacking scenario: untagged->singletagged
-				//TODO!! in theory there could be different rules running in setting different PCP/VID'S
-				//  for untagged/priotagged, last rule wins (and remains the only one), maybe that should be
-				//  checked already at flow-add (and rejected) - to be observed if such is possible in Voltha
-				//  delete now assumes there is only one such rule!
-				logger.Debugw("UniVlanConfigFsm Tx Set::EVTOCD reset untagged rule to default", log.Fields{
-					"device-id": oFsm.deviceID})
-				sliceEvtocdRule := make([]uint8, 16)
-				// fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
-				binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
-					cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
-						cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
-						cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
-
-				binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
-					cPrioIgnoreTag<<cFilterPrioOffset| // Not an inner-tag rule
-						cDoNotFilterVid<<cFilterVidOffset| // Do not filter on inner vid
-						cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
-						cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
-
-				binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:],
-					0<<cTreatTTROffset| // Do not pop any tags
-						cDoNotAddPrio<<cTreatPrioOffset| // do not add outer tag
-						cDontCareVid<<cTreatVidOffset| // Outer VID don't care
-						cDontCareTpid<<cTreatTpidOffset) // Outer TPID field don't care
-
-				binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:],
-					cDoNotAddPrio<<cTreatPrioOffset| // do not add inner tag
-						cDontCareVid<<cTreatVidOffset| // Outer VID don't care
-						cDontCareTpid<<cTreatTpidOffset) // copy TPID and DEI
-
+			// VOL-3685
+			// NOTE: With ALPHA ONUs it was seen that just resetting a particular entry in the EVTOCD table
+			// and re-configuring a new entry would not work. The old entry is removed and new entry is created
+			// indeed, but the traffic landing upstream would carry old vlan sometimes.
+			// This is only a WORKAROUND which basically deletes the entire EVTOCD ME and re-creates it again
+			// later when the flow is being re-installed.
+			// Of course this is applicable to case only where single service (or single tcont) is in use and
+			// there is only one service vlan (oFsm.acceptIncrementalEvtoOption is false in this case).
+			// Interstingly this problem has not been observed in multi-tcont (or multi-service) scenario (in
+			// which case the oFsm.acceptIncrementalEvtoOption is set to true).
+			if oFsm.configuredUniFlow == 0 && !oFsm.acceptIncrementalEvtoOption {
+				logger.Debugw("UniVlanConfigFsm Tx Remove::EVTOCD", log.Fields{"device-id": oFsm.deviceID})
+				// When there are no more EVTOCD vlan configurations on the ONU and acceptIncrementalEvtoOption
+				// is not enabled, delete the EVTOCD ME.
 				meParams := me.ParamData{
 					EntityID: oFsm.evtocdID,
-					Attributes: me.AttributeValueMap{
-						"ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
-					},
 				}
-				meInstance := oFsm.pOmciCC.sendSetEvtocdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+				meInstance := oFsm.pOmciCC.sendDeleteEvtocd(context.TODO(), ConstDefaultOmciTimeout, true,
 					oFsm.pAdaptFsm.commChan, meParams)
 				//accept also nil as (error) return value for writing to LastTx
 				//  - this avoids misinterpretation of new received OMCI messages
@@ -1586,52 +1653,111 @@
 				//verify response
 				err := oFsm.waitforOmciResponse()
 				if err != nil {
-					logger.Errorw("Evtocd reset untagged rule to default failed, aborting VlanConfig FSM!",
+					logger.Errorw("Evtocd delete rule failed, aborting VlanConfig FSM!",
 						log.Fields{"device-id": oFsm.deviceID})
 					_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
 					return
 				}
-			} // just for local var's
-			{ // just for local var's
-				// this defines 'stacking' scenario: priotagged->singletagged
-				logger.Debugw("UniVlanConfigFsm Tx Set::EVTOCD delete priotagged rule", log.Fields{
-					"device-id": oFsm.deviceID})
-				sliceEvtocdRule := make([]uint8, 16)
-				// fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
-				binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
-					cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
-						cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
-						cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
+			} else {
+				// NOTE : We should ideally never ether this section when oFsm.acceptIncrementalEvtoOption is set to false
+				// This is true for only ATT/DT workflow
+				logger.Debugw("UniVlanConfigFsm: Remove EVTOCD set operation",
+					log.Fields{"configured-flow": oFsm.configuredUniFlow, "incremental-evto": oFsm.acceptIncrementalEvtoOption})
+				//not transparent and not acceptIncrementalEvtoOption: untagged/priotagged->singletagged
+				{ // just for local var's
+					// this defines stacking scenario: untagged->singletagged
+					//TODO!! in theory there could be different rules running in setting different PCP/VID'S
+					//  for untagged/priotagged, last rule wins (and remains the only one), maybe that should be
+					//  checked already at flow-add (and rejected) - to be observed if such is possible in Voltha
+					//  delete now assumes there is only one such rule!
+					logger.Debugw("UniVlanConfigFsm Tx Set::EVTOCD reset untagged rule to default", log.Fields{
+						"device-id": oFsm.deviceID})
+					sliceEvtocdRule := make([]uint8, 16)
+					// fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
+					binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
+						cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
+							cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
+							cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
 
-				binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
-					cPrioDoNotFilter<<cFilterPrioOffset| // Do not Filter on innerprio
-						0<<cFilterVidOffset| // filter on inner vid 0 (prioTagged)
-						cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
-						cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
+					binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
+						cPrioIgnoreTag<<cFilterPrioOffset| // Not an inner-tag rule
+							cDoNotFilterVid<<cFilterVidOffset| // Do not filter on inner vid
+							cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
+							cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
 
-				// delete indication for the indicated Filter
-				binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:], 0xFFFFFFFF)
-				binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:], 0xFFFFFFFF)
+					binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:],
+						0<<cTreatTTROffset| // Do not pop any tags
+							cDoNotAddPrio<<cTreatPrioOffset| // do not add outer tag
+							cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+							cDontCareTpid<<cTreatTpidOffset) // Outer TPID field don't care
 
-				meParams := me.ParamData{
-					EntityID: oFsm.evtocdID,
-					Attributes: me.AttributeValueMap{
-						"ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
-					},
-				}
-				meInstance := oFsm.pOmciCC.sendSetEvtocdVar(context.TODO(), ConstDefaultOmciTimeout, true,
-					oFsm.pAdaptFsm.commChan, meParams)
-				//accept also nil as (error) return value for writing to LastTx
-				//  - this avoids misinterpretation of new received OMCI messages
-				oFsm.pLastTxMeInstance = meInstance
+					binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:],
+						cDoNotAddPrio<<cTreatPrioOffset| // do not add inner tag
+							cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+							cDontCareTpid<<cTreatTpidOffset) // copy TPID and DEI
 
-				//verify response
-				err := oFsm.waitforOmciResponse()
-				if err != nil {
-					logger.Errorw("Evtocd delete priotagged rule failed, aborting VlanConfig FSM!",
-						log.Fields{"device-id": oFsm.deviceID})
-					_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
-					return
+					meParams := me.ParamData{
+						EntityID: oFsm.evtocdID,
+						Attributes: me.AttributeValueMap{
+							"ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+						},
+					}
+					meInstance := oFsm.pOmciCC.sendSetEvtocdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+						oFsm.pAdaptFsm.commChan, meParams)
+					//accept also nil as (error) return value for writing to LastTx
+					//  - this avoids misinterpretation of new received OMCI messages
+					oFsm.pLastTxMeInstance = meInstance
+
+					//verify response
+					err := oFsm.waitforOmciResponse()
+					if err != nil {
+						logger.Errorw("Evtocd reset untagged rule to default failed, aborting VlanConfig FSM!",
+							log.Fields{"device-id": oFsm.deviceID})
+						_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
+						return
+					}
+				} // just for local var's
+				{ // just for local var's
+					// this defines 'stacking' scenario: priotagged->singletagged
+					logger.Debugw("UniVlanConfigFsm Tx Set::EVTOCD delete priotagged rule", log.Fields{
+						"device-id": oFsm.deviceID})
+					sliceEvtocdRule := make([]uint8, 16)
+					// fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
+					binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
+						cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
+							cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
+							cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
+
+					binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
+						cPrioDoNotFilter<<cFilterPrioOffset| // Do not Filter on innerprio
+							0<<cFilterVidOffset| // filter on inner vid 0 (prioTagged)
+							cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
+							cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
+
+					// delete indication for the indicated Filter
+					binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:], 0xFFFFFFFF)
+					binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:], 0xFFFFFFFF)
+
+					meParams := me.ParamData{
+						EntityID: oFsm.evtocdID,
+						Attributes: me.AttributeValueMap{
+							"ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+						},
+					}
+					meInstance := oFsm.pOmciCC.sendSetEvtocdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+						oFsm.pAdaptFsm.commChan, meParams)
+					//accept also nil as (error) return value for writing to LastTx
+					//  - this avoids misinterpretation of new received OMCI messages
+					oFsm.pLastTxMeInstance = meInstance
+
+					//verify response
+					err := oFsm.waitforOmciResponse()
+					if err != nil {
+						logger.Errorw("Evtocd delete priotagged rule failed, aborting VlanConfig FSM!",
+							log.Fields{"device-id": oFsm.deviceID})
+						_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
+						return
+					}
 				}
 			} //just for local var's
 		}
@@ -1660,3 +1786,206 @@
 		return fmt.Errorf("uniVlanConfigFsm multi entity responseError %s", oFsm.deviceID)
 	}
 }
+
+func (oFsm *UniVlanConfigFsm) performSettingMulticastME(tpID uint16, multicastGemPortID uint16, vlanID uint32) error {
+	logger.Debugw("Setting Multicast MEs", log.Fields{"device-id": oFsm.deviceID, "tpID": tpID,
+		"multicastGemPortID": multicastGemPortID, "vlanID": vlanID})
+	errCreateMOP := oFsm.performCreatingMulticastOperationProfile()
+	if errCreateMOP != nil {
+		logger.Errorw("MulticastOperationProfile create failed, aborting AniConfig FSM!",
+			log.Fields{"device-id": oFsm.deviceID})
+		_ = oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
+		return fmt.Errorf("creatingMulticastSubscriberConfigInfo responseError %s, error %s", oFsm.deviceID, errCreateMOP)
+	}
+
+	errSettingMOP := oFsm.performSettingMulticastOperationProfile(multicastGemPortID, vlanID)
+	if errSettingMOP != nil {
+		logger.Errorw("MulticastOperationProfile setting failed, aborting AniConfig FSM!",
+			log.Fields{"device-id": oFsm.deviceID})
+		_ = oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
+		return fmt.Errorf("creatingMulticastSubscriberConfigInfo responseError %s, error %s", oFsm.deviceID, errSettingMOP)
+	}
+
+	errCreateMSCI := oFsm.performCreatingMulticastSubscriberConfigInfo()
+	if errCreateMSCI != nil {
+		logger.Errorw("MulticastOperationProfile setting failed, aborting AniConfig FSM!",
+			log.Fields{"device-id": oFsm.deviceID})
+		_ = oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
+		return fmt.Errorf("creatingMulticastSubscriberConfigInfo responseError %s, error %s", oFsm.deviceID, errCreateMSCI)
+	}
+
+	meParams := me.ParamData{
+		EntityID: macBridgeServiceProfileEID + uint16(oFsm.pOnuUniPort.macBpNo),
+		Attributes: me.AttributeValueMap{
+			"BridgeIdPointer": macBridgeServiceProfileEID + uint16(oFsm.pOnuUniPort.macBpNo),
+			"PortNum":         0xf0, //fixed unique ANI side indication
+			"TpType":          6,    //MCGemIWTP
+			"TpPointer":       multicastGemPortID,
+		},
+	}
+	meInstance := oFsm.pOmciCC.sendCreateMBPConfigDataVar(context.TODO(), ConstDefaultOmciTimeout, true,
+		oFsm.pAdaptFsm.commChan, meParams)
+	//accept also nil as (error) return value for writing to LastTx
+	//  - this avoids misinterpretation of new received OMCI messages
+	oFsm.pLastTxMeInstance = meInstance
+	err := oFsm.waitforOmciResponse()
+	if err != nil {
+		logger.Errorw("CreateMBPConfigData failed, aborting AniConfig FSM!",
+			log.Fields{"device-id": oFsm.deviceID, "MBPConfigDataID": macBridgeServiceProfileEID})
+		_ = oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
+		return fmt.Errorf("creatingMulticastSubscriberConfigInfo responseError %s, error %s", oFsm.deviceID, err)
+	}
+
+	// ==> Start creating VTFD for mcast vlan
+
+	// This attribute uniquely identifies each instance of this managed entity. Through an identical ID,
+	// this managed entity is implicitly linked to an instance of the MAC bridge port configuration data ME.
+	mcastVtfdID := macBridgeServiceProfileEID + uint16(oFsm.pOnuUniPort.macBpNo)
+
+	logger.Debugw("UniVlanConfigFsm set VTFD for mcast", log.Fields{
+		"EntitytId": strconv.FormatInt(int64(mcastVtfdID), 16), "mcastVlanID": vlanID,
+		"in state": oFsm.pAdaptFsm.pFsm.Current(), "device-id": oFsm.deviceID})
+	vtfdFilterList := make([]uint16, cVtfdTableSize) //needed for parameter serialization
+
+	// FIXME: VOL-3685: Issues with resetting a table entry in EVTOCD ME
+	// VTFD has to be created afresh with a new entity ID that has the same entity ID as the MBPCD ME for every
+	// new vlan associated with a different TP.
+	vtfdFilterList[0] = uint16(vlanID)
+
+	meParams = me.ParamData{
+		EntityID: mcastVtfdID,
+		Attributes: me.AttributeValueMap{
+			"VlanFilterList":   vtfdFilterList,
+			"ForwardOperation": uint8(0x10), //VID investigation
+			"NumberOfEntries":  oFsm.numVlanFilterEntries,
+		},
+	}
+	meInstance = oFsm.pOmciCC.sendCreateVtfdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+		oFsm.pAdaptFsm.commChan, meParams)
+	oFsm.pLastTxMeInstance = meInstance
+	err = oFsm.waitforOmciResponse()
+	if err != nil {
+		logger.Errorw("CreateMcastVlanFilterData failed, aborting AniConfig FSM!",
+			log.Fields{"device-id": oFsm.deviceID, "mcastVtfdID": mcastVtfdID})
+		_ = oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
+		return fmt.Errorf("createMcastVlanFilterData responseError %s, error %s", oFsm.deviceID, err)
+	}
+
+	return nil
+}
+
+func (oFsm *UniVlanConfigFsm) performCreatingMulticastSubscriberConfigInfo() error {
+	instID, err := oFsm.pDeviceHandler.getUniPortMEEntityID(oFsm.pOnuUniPort.portNo)
+	if err != nil {
+		log.Errorw("error fetching uni port me instance",
+			log.Fields{"device-id": oFsm.deviceID, "portNo": oFsm.pOnuUniPort.portNo})
+		return err
+	}
+	meParams := me.ParamData{
+		EntityID: instID,
+		Attributes: me.AttributeValueMap{
+			"MeType": 0,
+			//Direct reference to the Operation profile
+			//TODO ANI side used on UNI side, not the clearest option.
+			"MulticastOperationsProfilePointer": macBridgePortAniEID + uint16(oFsm.pOnuUniPort.macBpNo),
+		},
+	}
+	meInstance := oFsm.pOmciCC.sendCreateMulticastSubConfigInfoVar(context.TODO(), ConstDefaultOmciTimeout, true,
+		oFsm.pAdaptFsm.commChan, meParams)
+	//accept also nil as (error) return value for writing to LastTx
+	//  - this avoids misinterpretation of new received OMCI messages
+	oFsm.pLastTxMeInstance = meInstance
+	//verify response
+	err = oFsm.waitforOmciResponse()
+	if err != nil {
+		logger.Errorw("CreateMulticastSubConfigInfo create failed, aborting AniConfig FSM!",
+			log.Fields{"device-id": oFsm.deviceID, "MulticastSubConfigInfo": instID})
+		return fmt.Errorf("creatingMulticastSubscriberConfigInfo responseError %s", oFsm.deviceID)
+	}
+	return nil
+}
+
+func (oFsm *UniVlanConfigFsm) performCreatingMulticastOperationProfile() error {
+	instID := macBridgePortAniEID + uint16(oFsm.pOnuUniPort.macBpNo)
+	meParams := me.ParamData{
+		EntityID: instID,
+		Attributes: me.AttributeValueMap{
+			"IgmpVersion":  2,
+			"IgmpFunction": 0,
+			//0 means false
+			"ImmediateLeave":         0,
+			"Robustness":             2,
+			"QuerierIp":              0,
+			"QueryInterval":          125,
+			"QuerierMaxResponseTime": 100,
+			"LastMemberResponseTime": 10,
+			//0 means false
+			"UnauthorizedJoinBehaviour": 0,
+		},
+	}
+	meInstance := oFsm.pOmciCC.sendCreateMulticastOperationProfileVar(context.TODO(), ConstDefaultOmciTimeout, true,
+		oFsm.pAdaptFsm.commChan, meParams)
+	//accept also nil as (error) return value for writing to LastTx
+	//  - this avoids misinterpretation of new received OMCI messages
+	oFsm.pLastTxMeInstance = meInstance
+	//verify response
+	err := oFsm.waitforOmciResponse()
+	if err != nil {
+		logger.Errorw("CreateMulticastOperationProfile create failed, aborting AniConfig FSM!",
+			log.Fields{"device-id": oFsm.deviceID, "MulticastOperationProfileID": instID})
+		return fmt.Errorf("createMulticastOperationProfile responseError %s", oFsm.deviceID)
+	}
+	return nil
+}
+
+func (oFsm *UniVlanConfigFsm) performSettingMulticastOperationProfile(multicastGemPortID uint16, vlanID uint32) error {
+	instID := macBridgePortAniEID + uint16(oFsm.pOnuUniPort.macBpNo)
+	//TODO check that this is correct
+	// Table control
+	//setCtrl = 1
+	//rowPartId = 0
+	//test = 0
+	//rowKey = 0
+	tableCtrlStr := "0100000000000000"
+	tableCtrl := AsByteSlice(tableCtrlStr)
+	//TODO Building it as a Table, even though the attribute is `StringAttributeType`
+	// see line 56 of multicastoperationsprofileframe.go, it's an error in the conversion.
+	// FIXED 30/12/2020 Fixed for now with a local copy of multicastoperationsprofileframe.go in vendor/omci-lib-go
+	// provided by Chip, needs upstreaming and version change.
+	dynamicAccessCL := make([]uint8, 24)
+	copy(dynamicAccessCL, tableCtrl)
+	//Multicast GemPortId
+	binary.BigEndian.PutUint16(dynamicAccessCL[2:], multicastGemPortID)
+	// python version waits for installation of flows, see line 723 onward of
+	// brcm_openomci_onu_handler.py
+	binary.BigEndian.PutUint16(dynamicAccessCL[4:], uint16(vlanID))
+	//Source IP all to 0
+	binary.BigEndian.PutUint32(dynamicAccessCL[6:], IPToInt32(net.IPv4(0, 0, 0, 0)))
+	//TODO start and end are hardcoded, get from TP
+	// Destination IP address start of range
+	binary.BigEndian.PutUint32(dynamicAccessCL[10:], IPToInt32(net.IPv4(225, 0, 0, 0)))
+	// Destination IP address end of range
+	binary.BigEndian.PutUint32(dynamicAccessCL[14:], IPToInt32(net.IPv4(239, 255, 255, 255)))
+	//imputed group bandwidth
+	binary.BigEndian.PutUint16(dynamicAccessCL[18:], 0)
+
+	meParams := me.ParamData{
+		EntityID: instID,
+		Attributes: me.AttributeValueMap{
+			"DynamicAccessControlListTable": dynamicAccessCL,
+		},
+	}
+	meInstance := oFsm.pOmciCC.sendSetMulticastOperationProfileVar(context.TODO(), ConstDefaultOmciTimeout, true,
+		oFsm.pAdaptFsm.commChan, meParams)
+	//accept also nil as (error) return value for writing to LastTx
+	//  - this avoids misinterpretation of new received OMCI messages
+	oFsm.pLastTxMeInstance = meInstance
+	//verify response
+	err := oFsm.waitforOmciResponse()
+	if err != nil {
+		logger.Errorw("CreateMulticastOperationProfile create failed, aborting AniConfig FSM!",
+			log.Fields{"device-id": oFsm.deviceID, "MulticastOperationProfileID": instID})
+		return fmt.Errorf("createMulticastOperationProfile responseError %s", oFsm.deviceID)
+	}
+	return nil
+}
diff --git a/internal/pkg/onuadaptercore/onu_uni_tp.go b/internal/pkg/onuadaptercore/onu_uni_tp.go
index 1ab6c0b..2be93b9 100644
--- a/internal/pkg/onuadaptercore/onu_uni_tp.go
+++ b/internal/pkg/onuadaptercore/onu_uni_tp.go
@@ -425,22 +425,25 @@
 			uint8(content.Weight)
 	}
 
-	for pos, downstreamContent := range tpInst.DownstreamGemPortAttributeList {
-		if uint32(pos) == loNumGemPorts {
-			logger.Debugw("PonAniConfig abort GemPortList - GemList exceeds set NumberOfGemPorts",
-				log.Fields{"device-id": onuTP.deviceID, "index": pos, "NumGem": loNumGemPorts})
-			break
-		}
-
+	for _, downstreamContent := range tpInst.DownstreamGemPortAttributeList {
+		log.Debugw("Operating on Downstream Gem Port", log.Fields{"downstream-gem": downstreamContent})
+		//Commenting this out due to faliure, needs investigation
+		//if uint32(pos) == loNumGemPorts {
+		//	logger.Debugw("PonAniConfig abort GemPortList - GemList exceeds set NumberOfGemPorts",
+		//		log.Fields{"device-id": onuTP.deviceID, "index": pos, "NumGem": loNumGemPorts})
+		//	break
+		//}
+		isMulticast := false
 		//Flag is defined as string in the TP in voltha-lib-go, parsing it from string
-		isMulticast, err := strconv.ParseBool(downstreamContent.IsMulticast)
-
-		if err != nil {
-			logger.Errorw("multicast-error-config-unknown-flag-in-technology-profile", log.Fields{"UniTpKey": uniTPKey,
-				"downstream-gem": downstreamContent, "error": err})
-			continue
+		if downstreamContent.IsMulticast != "" {
+			isMulticast, err = strconv.ParseBool(downstreamContent.IsMulticast)
+			if err != nil {
+				logger.Errorw("multicast-error-config-unknown-flag-in-technology-profile",
+					log.Fields{"UniTpKey": uniTPKey, "downstream-gem": downstreamContent, "error": err})
+				continue
+			}
 		}
-
+		log.Infow("Gem Port is multicast", log.Fields{"isMulticast": isMulticast})
 		if isMulticast {
 			mcastGemID := uint16(downstreamContent.McastGemID)
 			_, existing := onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID]
@@ -451,7 +454,7 @@
 				continue
 			} else {
 				//GEM port is not configured, setting multicast attributes
-				logger.Infow("creating-multicast-gem-port", log.Fields{"uniPtKEy": uniTPKey,
+				logger.Infow("creating-multicast-gem-port", log.Fields{"uniTpKey": uniTPKey,
 					"gemPortId": mcastGemID, "key": mcastGemID})
 
 				//for all further GemPorts we need to extend the mapGemPortParams
@@ -460,13 +463,35 @@
 				//Separate McastGemId is derived from OMCI-lib-go, if not needed first needs to be removed there.
 				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].gemPortID = mcastGemID
 				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].direction = 2 // for ANI to UNI as defined in G.988
-				//Downstream Priority Queue is set in the data of any message exchanged, using in mcast too.
+
+				if downstreamContent.AesEncryption == "True" {
+					onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].gemPortEncState = 1
+				} else {
+					onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].gemPortEncState = 0
+				}
+
+				// expected Prio-Queue values 0..7 with 7 for highest PrioQueue, QueueIndex=Prio = 0..7
 				if downstreamContent.PriorityQueue > 7 {
 					logger.Errorw("PonAniConfig reject on GemPortList - PrioQueue value invalid",
-						log.Fields{"device-id": onuTP.deviceID, "index": pos, "PrioQueue": downstreamContent.PriorityQueue})
+						log.Fields{"device-id": onuTP.deviceID, "index": mcastGemID, "PrioQueue": downstreamContent.PriorityQueue})
+					//remove PonAniConfig  as done so far, delete map should be safe, even if not existing
+					delete(onuTP.mapPonAniConfig, uniTPKey)
+					onuTP.chTpConfigProcessingStep <- 0 //error indication
+					return
 				}
 				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].prioQueueIndex =
 					uint8(downstreamContent.PriorityQueue)
+				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].pbitString =
+					strings.TrimPrefix(downstreamContent.PbitMap, binaryStringPrefix)
+
+				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].discardPolicy =
+					downstreamContent.DiscardPolicy
+				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].queueSchedPolicy =
+					downstreamContent.SchedulingPolicy
+				//'GemWeight' looks strange in default profile, for now we just copy the weight to first queue
+				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].queueWeight =
+					uint8(downstreamContent.Weight)
+
 				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].isMulticast = isMulticast
 				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].multicastGemPortID =
 					uint16(downstreamContent.McastGemID)
@@ -836,3 +861,22 @@
 		onuTP.mapUniTpIndication[uniTpKey].techProfileToDelete = aState
 	} //else: the state is just ignored (does not exist)
 }
+
+// setProfileToDelete sets the requested techProfile toDelete state (if possible)
+func (onuTP *onuUniTechProf) getMulticastGemPorts(aUniID uint8, aTpID uint8) []uint16 {
+	uniTpKey := uniTP{uniID: aUniID, tpID: aTpID}
+	onuTP.mutexTPState.Lock()
+	defer onuTP.mutexTPState.Unlock()
+	gemPortIds := make([]uint16, 0)
+	if techProfile, existTP := onuTP.mapPonAniConfig[uniTpKey]; existTP {
+		for _, gemPortParam := range techProfile.mapGemPortParams {
+			if gemPortParam.isMulticast {
+				log.Debugw("Detected multicast gemPort", log.Fields{"device-id": onuTP.deviceID,
+					"aUniID": aUniID, "aTPID": aTpID, "uniTPKey": uniTpKey,
+					"mcastGemId": gemPortParam.multicastGemPortID})
+				gemPortIds = append(gemPortIds, gemPortParam.multicastGemPortID)
+			}
+		}
+	} //else: the state is just ignored (does not exist)
+	return gemPortIds
+}
diff --git a/internal/pkg/onuadaptercore/openonu_utils.go b/internal/pkg/onuadaptercore/openonu_utils.go
index 0db62ed..32eeed8 100644
--- a/internal/pkg/onuadaptercore/openonu_utils.go
+++ b/internal/pkg/onuadaptercore/openonu_utils.go
@@ -51,3 +51,23 @@
 	}
 	return binary.BigEndian.Uint32(ip)
 }
+
+//AsByteSlice transforms a string of manually set bits to a byt array
+func AsByteSlice(bitString string) []byte {
+	var out []byte
+	var str string
+
+	for i := len(bitString); i > 0; i -= 8 {
+		if i-8 < 0 {
+			str = bitString[0:i]
+		} else {
+			str = bitString[i-8 : i]
+		}
+		v, err := strconv.ParseUint(str, 2, 8)
+		if err != nil {
+			panic(err)
+		}
+		out = append([]byte{byte(v)}, out...)
+	}
+	return out
+}
diff --git a/vendor/github.com/opencord/omci-lib-go/VERSION b/vendor/github.com/opencord/omci-lib-go/VERSION
index 288adf5..dffa40e 100644
--- a/vendor/github.com/opencord/omci-lib-go/VERSION
+++ b/vendor/github.com/opencord/omci-lib-go/VERSION
@@ -1 +1 @@
-0.13.3
+0.13.4
diff --git a/vendor/github.com/opencord/omci-lib-go/generated/multicastoperationsprofileframe.go b/vendor/github.com/opencord/omci-lib-go/generated/multicastoperationsprofileframe.go
index 61f36fb..6204894 100644
--- a/vendor/github.com/opencord/omci-lib-go/generated/multicastoperationsprofileframe.go
+++ b/vendor/github.com/opencord/omci-lib-go/generated/multicastoperationsprofileframe.go
@@ -1,5 +1,4 @@
 /*
- * Copyright (c) 2018 - present.  Boling Consulting Solutions (bcsw.net)
  * Copyright 2020-present Open Networking Foundation
 
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,11 +14,9 @@
  * limitations under the License.
  */
 /*
-* NOTE: This file was generated, manual edits will be overwritten!
-*
-* Generated by 'goCodeGenerator.py':
-*              https://github.com/cboling/OMCI-parser/README.md
-*/
+* NOTE: This file was hand coded (not generated by omci-parser) due to complexity
+*       of the ME's entry in the G.988 (11/2017) specification.
+ */
 package generated
 
 import "github.com/deckarep/golang-set"
@@ -28,14 +25,14 @@
 
 var multicastoperationsprofileME *ManagedEntityDefinition
 
-type MulticastOperationsProfile struct{
+type MulticastOperationsProfile struct {
 	ManagedEntityDefinition
 	Attributes AttributeValueMap
 }
 
-func init(){
+func init() {
 	multicastoperationsprofileME = &ManagedEntityDefinition{
-		Name: "MulticastOperationsProfile",
+		Name:    "MulticastOperationsProfile",
 		ClassID: 309,
 		MessageTypes: mapset.NewSetWith(
 			Create,
@@ -43,28 +40,28 @@
 			Get,
 			Set,
 			GetNext,
-			),
+		),
 		AllowedAttributeMask: 0xffff,
 		AttributeDefinitions: AttributeDefinitionMap{
-			0: Uint16Field("ManagedEntityId", PointerAttributeType, 0x0000, 0, mapset.NewSetWith(Read, SetByCreate), false,false,false,0),
-			1: ByteField("IgmpVersion", EnumerationAttributeType, 0x8000, 0, mapset.NewSetWith(Read, SetByCreate, Write), false, false, false, 1),
-			2: ByteField("IgmpFunction", EnumerationAttributeType, 0x4000, 0, mapset.NewSetWith(Read, SetByCreate, Write), false, false, false, 2),
-			3: ByteField("ImmediateLeave", EnumerationAttributeType, 0x2000, 0, mapset.NewSetWith(Read, SetByCreate, Write), false, false, false, 3),
-			4: Uint16Field("USIgmpTci", PointerAttributeType, 0x1000, 0, mapset.NewSetWith(Read, SetByCreate, Write), false, true, false, 4),
-            5: ByteField("USIgmpTagCtrl", EnumerationAttributeType, 0x0800, 0, mapset.NewSetWith(Read, SetByCreate, Write), false, true, false, 5),
-			6: Uint32Field("USIgmpRate", CounterAttributeType, 0x0400, 0, mapset.NewSetWith(Read), false, true, false, 6),
-			7: MultiByteField("DynamicAccessControlListTable", StringAttributeType, 0x0200, 24, toOctets("AAAAAAAAAAAAAAAAAAAAAAAAAAA="), mapset.NewSetWith(Read), false, false, false, 7),
-			8: MultiByteField("StaticAccessControlListTable", StringAttributeType, 0x0100, 24, toOctets("AAAAAAAAAAAAAAAAAAAAAAAAAAA="), mapset.NewSetWith(Read), false, true, false, 8),
-			9: MultiByteField("LostGroupsListTable", StringAttributeType, 0x0080, 10, toOctets("AAAAAAAAAAAAAAAAAAAAAAAAAAA="), mapset.NewSetWith(Read), false, true, false, 9),
-			10: ByteField("Robustness", EnumerationAttributeType, 0x0040, 0, mapset.NewSetWith(Read, SetByCreate, Write), false, false, false, 10),
-			11: Uint32Field("QuerierIp", CounterAttributeType, 0x0020, 0, mapset.NewSetWith(Read), false, false, false, 11),
-			12: Uint32Field("QueryInterval", CounterAttributeType, 0x0010, 0, mapset.NewSetWith(Read), false, false, false, 12),
-			13: Uint32Field("QuerierMaxResponseTime", CounterAttributeType, 0x0008, 0, mapset.NewSetWith(Read), false, false, false, 13),
-			14: Uint32Field("LastMemberResponseTime", CounterAttributeType, 0x0004, 0, mapset.NewSetWith(Read), false, false, false, 14),
-			15: ByteField("UnauthorizedJoinBehaviour", EnumerationAttributeType, 0x0002, 0, mapset.NewSetWith(Read, SetByCreate, Write), false, false, false, 15),
-			16: MultiByteField("DSIgmpMcastTci", StringAttributeType, 0x0001, 10, toOctets("AAAAAAAAAAAAAAAAAAAAAAAAAAA="), mapset.NewSetWith(Read), false, true, false, 16),
+			0:  Uint16Field("ManagedEntityId", PointerAttributeType, 0x0000, 0, mapset.NewSetWith(Read, SetByCreate), false, false, false, 0),
+			1:  ByteField("IgmpVersion", EnumerationAttributeType, 0x8000, 0, mapset.NewSetWith(Read, SetByCreate, Write), false, false, false, 1),
+			2:  ByteField("IgmpFunction", EnumerationAttributeType, 0x4000, 0, mapset.NewSetWith(Read, SetByCreate, Write), false, false, false, 2),
+			3:  ByteField("ImmediateLeave", EnumerationAttributeType, 0x2000, 0, mapset.NewSetWith(Read, SetByCreate, Write), false, false, false, 3),
+			4:  Uint16Field("USIgmpTci", UnsignedIntegerAttributeType, 0x1000, 0, mapset.NewSetWith(Read, SetByCreate, Write), false, true, false, 4),
+			5:  ByteField("USIgmpTagCtrl", EnumerationAttributeType, 0x0800, 0, mapset.NewSetWith(Read, SetByCreate, Write), false, true, false, 5),
+			6:  Uint32Field("USIgmpRate", UnsignedIntegerAttributeType, 0x0400, 0, mapset.NewSetWith(Read, SetByCreate, Write), false, true, false, 6),
+			7:  TableField("DynamicAccessControlListTable", TableAttributeType, 0x0200, TableInfo{nil, 24}, mapset.NewSetWith(Read, Write), false, false, false, 7),
+			8:  TableField("StaticAccessControlListTable", TableAttributeType, 0x0100, TableInfo{nil, 24}, mapset.NewSetWith(Read, Write), false, true, false, 8),
+			9:  TableField("LostGroupsListTable", TableAttributeType, 0x0080, TableInfo{nil, 10}, mapset.NewSetWith(Read), false, true, false, 9),
+			10: ByteField("Robustness", UnsignedIntegerAttributeType, 0x0040, 0, mapset.NewSetWith(Read, SetByCreate, Write), false, true, false, 10),
+			11: Uint32Field("QuerierIp", UnsignedIntegerAttributeType, 0x0020, 0, mapset.NewSetWith(Read, SetByCreate, Write), false, true, false, 11),
+			12: Uint32Field("QueryInterval", UnsignedIntegerAttributeType, 0x0010, 0, mapset.NewSetWith(Read, SetByCreate, Write), false, true, false, 12),
+			13: Uint32Field("QuerierMaxResponseTime", UnsignedIntegerAttributeType, 0x0008, 0, mapset.NewSetWith(Read, SetByCreate, Write), false, true, false, 13),
+			14: Uint32Field("LastMemberResponseTime", UnsignedIntegerAttributeType, 0x0004, 0, mapset.NewSetWith(Read, Write), false, true, false, 14),
+			15: ByteField("UnauthorizedJoinBehaviour", UnsignedIntegerAttributeType, 0x0002, 0, mapset.NewSetWith(Read, Write), false, true, false, 15),
+			16: MultiByteField("DSIgmpMcastTci", OctetsAttributeType, 0x0001, 3, toOctets("AAAA"), mapset.NewSetWith(Read, SetByCreate, Write), false, true, false, 16),
 		},
-		Access: CreatedByOlt,
+		Access:  CreatedByOlt,
 		Support: UnknownSupport,
 	}
 }
@@ -72,6 +69,6 @@
 // NewMulticastOperationsProfilePoint (class ID 309) creates the basic
 // Managed Entity definition that is used to validate an ME of this type that
 // is received from or transmitted to the OMCC.
-func NewMulticastOperationsProfile(params ...ParamData) (*ManagedEntity, OmciErrors){
+func NewMulticastOperationsProfile(params ...ParamData) (*ManagedEntity, OmciErrors) {
 	return NewManagedEntity(*multicastoperationsprofileME, params...)
-}
\ No newline at end of file
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 8d18d5c..ba226fa 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -71,7 +71,7 @@
 github.com/mitchellh/go-homedir
 # github.com/mitchellh/mapstructure v1.1.2
 github.com/mitchellh/mapstructure
-# github.com/opencord/omci-lib-go v0.13.3
+# github.com/opencord/omci-lib-go v0.13.4
 github.com/opencord/omci-lib-go
 github.com/opencord/omci-lib-go/generated
 # github.com/opencord/voltha-lib-go/v3 v3.1.23