VOL-4338: Relaxed MIB Upload Next response decoding

Change-Id: I4c0db4d4786a1d8501daec18a6980821a9267b84
diff --git a/relaxed_decode_test.go b/relaxed_decode_test.go
new file mode 100644
index 0000000..75714be
--- /dev/null
+++ b/relaxed_decode_test.go
@@ -0,0 +1,575 @@
+/*
+ * 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");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package omci_test
+
+import (
+	"github.com/google/gopacket"
+	. "github.com/opencord/omci-lib-go"
+	me "github.com/opencord/omci-lib-go/generated"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+var relaxDecodeSupportedResponses = []me.MsgType{
+	// me.Get,
+	me.MibUploadNext,
+}
+
+func canRelax(arr []me.MsgType, msgType me.MsgType) bool {
+	for _, item := range arr {
+		if item == msgType {
+			return true
+		}
+	}
+	return false
+}
+
+func TestAllTypesRelaxedDecodeSupport(t *testing.T) {
+	// Requests (none are supported yet)
+	for _, msgType := range allMsgTypes {
+		assert.Error(t, me.SetRelaxedDecode(msgType, true, true))
+		assert.False(t, me.GetRelaxedDecode(msgType, true))
+	}
+	// Responses (only a couple are supported at this time)
+	for _, msgType := range allMsgTypes {
+
+		if canRelax(relaxDecodeSupportedResponses, msgType) {
+			// Default is True if it is supported
+			assert.True(t, me.GetRelaxedDecode(msgType, false))
+
+			// Set False
+			assert.NoError(t, me.SetRelaxedDecode(msgType, false, false))
+			assert.False(t, me.GetRelaxedDecode(msgType, false))
+
+			// Set back to True
+			assert.NoError(t, me.SetRelaxedDecode(msgType, false, true))
+			assert.True(t, me.GetRelaxedDecode(msgType, false))
+		} else {
+			// Default is False
+			assert.False(t, me.GetRelaxedDecode(msgType, false))
+			assert.Error(t, me.SetRelaxedDecode(msgType, false, true))
+			assert.Error(t, me.SetRelaxedDecode(msgType, false, false))
+		}
+	}
+}
+
+// TestMibUploadNextResponseRelaxedDecode will decode a frame with 'new' unknown
+// attributes at the end (fake ones for this test) and should fail. Then it will
+// enable relax decode and should be able to successfully decode the parts that
+// it knows and have access to the rest.
+func TestMibUploadNextResponseRelaxedDecode(t *testing.T) {
+	// Test msg has OLT-G ME that normally only has 4 attributes defined. Since several are
+	// pretty big and would normally take at least 3 MIB upload next frames.  So in
+	// this one it has the last 'known' one, plus two new ones.
+	extraTrailer := "123400001234000000000000"
+	goodAttribute := "123456780a090807060504030201"
+	mibUploadNextLayer := "00020000008300001c00" + goodAttribute + extraTrailer
+	goodMessage := "02862e0a" + mibUploadNextLayer + "00000028"
+
+	data, err := stringToPacket(goodMessage)
+	assert.NoError(t, err)
+
+	extraTrailerData, _ := stringToPacket(extraTrailer)
+	assert.NotNil(t, extraTrailerData)
+
+	mibUploadNextLayerData, _ := stringToPacket(mibUploadNextLayer)
+	assert.NotNil(t, mibUploadNextLayerData)
+
+	goodAttributeData, _ := stringToPacket(goodAttribute)
+	assert.NotNil(t, goodAttributeData)
+
+	// Make sure relaxed decode is disabled
+	assert.NoError(t, me.SetRelaxedDecode(me.MibUploadNext, false, false))
+	assert.False(t, me.GetRelaxedDecode(me.MibUploadNext, false))
+
+	// Should get a packet but there should also be an error layer after the OMCI layer
+	packet := gopacket.NewPacket(data, LayerTypeOMCI, gopacket.NoCopy)
+	assert.NotNil(t, packet)
+
+	omciLayer := packet.Layer(LayerTypeOMCI)
+	assert.NotNil(t, omciLayer)
+
+	omciMsg, ok := omciLayer.(*OMCI)
+	assert.True(t, ok)
+	assert.NotNil(t, omciMsg)
+
+	assert.Equal(t, LayerTypeOMCI, omciMsg.LayerType())
+	assert.Equal(t, LayerTypeOMCI, omciMsg.CanDecode())
+	assert.Equal(t, LayerTypeMibUploadNextResponse, omciMsg.NextLayerType())
+	assert.Equal(t, uint16(0x0286), omciMsg.TransactionID)
+	assert.Equal(t, MibUploadNextResponseType, omciMsg.MessageType)
+	assert.Equal(t, BaselineIdent, omciMsg.DeviceIdentifier)
+	assert.Equal(t, uint16(40), omciMsg.Length)
+
+	// Without relaxed decode, the MIB Upload Next Response cannot be decoded further
+	// but can get the error layer and it's contents (which is the entire MIB Upload Response data
+	msgLayer := packet.Layer(LayerTypeMibUploadNextResponse)
+	assert.Nil(t, msgLayer)
+
+	errLayer := packet.Layer(gopacket.LayerTypeDecodeFailure)
+	assert.NotNil(t, errLayer)
+	assert.Nil(t, errLayer.LayerPayload())
+	errContents := errLayer.LayerContents()
+	assert.NotNil(t, errContents)
+	assert.Equal(t, mibUploadNextLayerData, errContents)
+
+	////////////////////////////////////////////////////////////////////////////
+	////////////////////////////////////////////////////////////////////////////
+	////////////////////////////////////////////////////////////////////////////
+	// Now turn on relaxed decode and you can now go further into the packet
+	assert.NoError(t, me.SetRelaxedDecode(me.MibUploadNext, false, true))
+	assert.True(t, me.GetRelaxedDecode(me.MibUploadNext, false))
+
+	packet = gopacket.NewPacket(data, LayerTypeOMCI, gopacket.NoCopy)
+	assert.NotNil(t, packet)
+
+	omciLayer = packet.Layer(LayerTypeOMCI)
+	assert.NotNil(t, omciLayer)
+
+	omciMsg, ok = omciLayer.(*OMCI)
+	assert.True(t, ok)
+	assert.NotNil(t, omciMsg)
+
+	// Skipping the test of OMCI layer values. It is same as above
+	//
+	// Get that message layer that has data that could be decoded. If relaxed decode was
+	// not enable, this would have returned a 'nil' value
+	msgLayer = packet.Layer(LayerTypeMibUploadNextResponse)
+	assert.NotNil(t, msgLayer)
+
+	response, ok2 := msgLayer.(*MibUploadNextResponse)
+	assert.True(t, ok2)
+	assert.NotNil(t, response)
+	assert.Equal(t, LayerTypeMibUploadNextResponse, response.LayerType())
+	assert.Equal(t, LayerTypeMibUploadNextResponse, response.CanDecode())
+	assert.Equal(t, me.OltGClassID, response.ReportedME.GetClassID())
+	assert.Equal(t, uint16(0), response.ReportedME.GetEntityID())
+
+	// The attribute mask decoded at this layer only contains the attributes we
+	// could successfully decode
+	assert.Equal(t, uint16(0x1000), response.ReportedME.GetAttributeMask())
+
+	attributes := me.AttributeValueMap{
+		"TimeOfDayInformation": goodAttributeData, // NOTE: This is binary data for the comparison below
+	}
+	for name, value := range attributes {
+		pktValue, err := response.ReportedME.GetAttribute(name)
+		assert.Nil(t, err)
+		assert.Equal(t, pktValue, value)
+	}
+	assert.Nil(t, response.AdditionalMEs)
+
+	////////////////////////////////////////////////////////////////////////////
+	// Here is the new functionality.  In order to store both a well decoded
+	// MIB UPLOAD NEXT response layer, along with a relaxed decode error, the
+	// layer addition has to be done in a specific way and an error returned.
+	//
+	//     Note that the previous line (below) worked in the code above
+	//
+	//           response, ok2 := msgLayer.(*MibUploadNextResponse)
+	//
+	// If you did not care about what the relaxed decode found out, just proceed
+	// on as normal.  However, since you enabled relaxed decoding of the unknown
+	// attributes, here is where you can pull extra information from.
+	//
+	//  The first way is to just try and see if that error layer was decoded
+	//
+	//      if unknownAttrLayer = packet.Layer(LayerTypeUnknownAttributes); unknownAttrLayer != nil {
+	//          log.warning(HEY! Got some unknown attributes to this ME: '%v', unknownAttrLayer)
+	//
+	//          unknownAttributes, ok2 := msgLayer.(*UnknownAttributes); ok {
+	//				//
+	//				// Since some extended messages can actually return multiple managed entities,
+	//              // all ME's with unknown attributes need to be uniquely identified
+	//              //
+	//              for _, unknown := range unknownAttibutes.Attributes {
+	//                  whichME     := unknown.EntityClass			// ClassID
+	//                  whichInst   := unknown.EntityInstance		// uint16
+	//					unknownMask := unknown.AttributeMask		// uint16
+	//					unknownBlob := unknown.Attributes			// []byte
+	//
+	//                  // Unless this is the extended message set and only a single attribute
+	//              	// mask bit is set, you really do not know what possible kind of data
+	//              	// type the attribute is...
+	//               }
+	//	         }
+	//       }
+	/////////////////////////////////////
+	assert.NotEqual(t, gopacket.LayerTypePayload, response.NextLayerType())
+	assert.Equal(t, LayerTypeUnknownAttributes, response.NextLayerType())
+
+	unknownAttrLayer := packet.Layer(LayerTypeUnknownAttributes)
+	assert.NotNil(t, unknownAttrLayer)
+
+	unknown, ok2 := unknownAttrLayer.(*UnknownAttributes)
+	assert.True(t, ok2)
+	assert.NotNil(t, unknown)
+	assert.Equal(t, LayerTypeUnknownAttributes, unknown.LayerType())
+	assert.Equal(t, LayerTypeUnknownAttributes, unknown.CanDecode())
+	assert.Equal(t, gopacket.LayerTypeZero, unknown.NextLayerType())
+
+	// Only one Managed entity was in this response and had a bad attribute
+	assert.Equal(t, 1, len(unknown.Attributes))
+	assert.Equal(t, me.OltGClassID, unknown.Attributes[0].EntityClass)
+	assert.Equal(t, uint16(0), unknown.Attributes[0].EntityInstance)
+	assert.Equal(t, uint16(0x0c00), unknown.Attributes[0].AttributeMask)
+	assert.Equal(t, extraTrailerData, unknown.Attributes[0].AttributeData)
+}
+
+// TestMibUploadNextResponseExtendedRelaxedDecode is the extended message
+// set test of the test above (just one Managed Entity)
+func TestMibUploadNextResponseExtendedRelaxedDecode(t *testing.T) {
+	// Test msg has OLT-G ME that normally only has 4 attributes defined. Since several are
+	// pretty big and would normally take at least 3 MIB upload next frames.  So in
+	// this one it has the last 'known' one, plus two new ones.
+	extraTrailer := "123400001234"                  // 6 octets
+	goodAttribute := "123456780a090807060504030201" // 14 octets
+	mibUploadNextLayer := "00020000" + "001c" +
+		"0014" +
+		"008300001c00" + // 6 octets
+		goodAttribute + extraTrailer // 14 + 6 octets
+	goodMessage := "02862e0b" + mibUploadNextLayer
+
+	data, err := stringToPacket(goodMessage)
+	assert.NoError(t, err)
+
+	extraTrailerData, _ := stringToPacket(extraTrailer)
+	assert.NotNil(t, extraTrailerData)
+
+	mibUploadNextLayerData, _ := stringToPacket(mibUploadNextLayer)
+	assert.NotNil(t, mibUploadNextLayerData)
+
+	goodAttributeData, _ := stringToPacket(goodAttribute)
+	assert.NotNil(t, goodAttributeData)
+
+	// Make sure relaxed decode is disabled
+	assert.NoError(t, me.SetRelaxedDecode(me.MibUploadNext, false, false))
+	assert.False(t, me.GetRelaxedDecode(me.MibUploadNext, false))
+
+	// Should get a packet but there should also be an error layer after the OMCI layer
+	packet := gopacket.NewPacket(data, LayerTypeOMCI, gopacket.NoCopy)
+	assert.NotNil(t, packet)
+
+	omciLayer := packet.Layer(LayerTypeOMCI)
+	assert.NotNil(t, omciLayer)
+
+	omciMsg, ok := omciLayer.(*OMCI)
+	assert.True(t, ok)
+	assert.NotNil(t, omciMsg)
+
+	assert.Equal(t, LayerTypeOMCI, omciMsg.LayerType())
+	assert.Equal(t, LayerTypeOMCI, omciMsg.CanDecode())
+	assert.Equal(t, LayerTypeMibUploadNextResponse, omciMsg.NextLayerType())
+	assert.Equal(t, uint16(0x0286), omciMsg.TransactionID)
+	assert.Equal(t, MibUploadNextResponseType, omciMsg.MessageType)
+	assert.Equal(t, ExtendedIdent, omciMsg.DeviceIdentifier)
+	assert.Equal(t, uint16(28), omciMsg.Length)
+
+	// Without relaxed decode, the MIB Upload Next Response cannot be decoded further
+	// but can get the error layer and it's contents (which is the entire MIB Upload Response data
+	msgLayer := packet.Layer(LayerTypeMibUploadNextResponse)
+	assert.Nil(t, msgLayer)
+
+	errLayer := packet.Layer(gopacket.LayerTypeDecodeFailure)
+	assert.NotNil(t, errLayer)
+	assert.Nil(t, errLayer.LayerPayload())
+	errContents := errLayer.LayerContents()
+	assert.NotNil(t, errContents)
+	assert.Equal(t, mibUploadNextLayerData, errContents)
+
+	////////////////////////////////////////////////////////////////////////////
+	////////////////////////////////////////////////////////////////////////////
+	////////////////////////////////////////////////////////////////////////////
+	// Now turn on relaxed decode and you can now go further into the packet
+	assert.NoError(t, me.SetRelaxedDecode(me.MibUploadNext, false, true))
+	assert.True(t, me.GetRelaxedDecode(me.MibUploadNext, false))
+
+	packet = gopacket.NewPacket(data, LayerTypeOMCI, gopacket.NoCopy)
+	assert.NotNil(t, packet)
+
+	omciLayer = packet.Layer(LayerTypeOMCI)
+	assert.NotNil(t, omciLayer)
+
+	omciMsg, ok = omciLayer.(*OMCI)
+	assert.True(t, ok)
+	assert.NotNil(t, omciMsg)
+
+	// Skipping the test of OMCI layer values. It is same as above
+	//
+	// Get that message layer that has data that could be decoded
+	msgLayer = packet.Layer(LayerTypeMibUploadNextResponse)
+	assert.NotNil(t, msgLayer)
+
+	response, ok2 := msgLayer.(*MibUploadNextResponse)
+	assert.True(t, ok2)
+	assert.NotNil(t, response)
+	assert.Equal(t, LayerTypeMibUploadNextResponse, response.LayerType())
+	assert.Equal(t, LayerTypeMibUploadNextResponse, response.CanDecode())
+	assert.Equal(t, me.OltGClassID, response.ReportedME.GetClassID())
+	assert.Equal(t, uint16(0), response.ReportedME.GetEntityID())
+
+	// The attribute mask decoded at this layer only contains the attributes we
+	// could successfully decode
+	assert.Equal(t, uint16(0x1000), response.ReportedME.GetAttributeMask())
+
+	attributes := me.AttributeValueMap{
+		"TimeOfDayInformation": goodAttributeData, // NOTE: This is binary data for the comparison below
+	}
+	for name, value := range attributes {
+		pktValue, err := response.ReportedME.GetAttribute(name)
+		assert.Nil(t, err)
+		assert.Equal(t, pktValue, value)
+	}
+	////////////////////////////////////////////////////////////////////////////
+	// Here is the new functionality.  In order to store both a well decoded
+	// MIB UPLOAD NEXT response layer, along with a relaxed decode error, the
+	// layer addition has to be done in a specific way and an error returned.
+	//
+	//     Note that the previous line (below) worked in the code above
+	//
+	//           response, ok2 := msgLayer.(*MibUploadNextResponse)
+	//
+	// If you did not care about what the relaxed decode found out, just proceed
+	// on as normal.  However, since you enabled relaxed decoding of the unknown
+	// attributes, here is where you can pull extra information from.
+	//
+	//  The first way is to just try and see if that error layer was decoded
+	//
+	//      if unknownAttrLayer = packet.Layer(LayerTypeUnknownAttributes); unknownAttrLayer != nil {
+	//          log.warning(HEY! Got some unknown attributes to this ME: '%v', unknownAttrLayer)
+	//
+	//          unknownAttributes, ok2 := msgLayer.(*UnknownAttributes); ok {
+	//				//
+	//				// Since some extended messages can actually return multiple managed entities,
+	//              // all ME's with unknown attributes need to be uniquely identified
+	//              //
+	//              for _, unknown := range unknownAttibutes.Attributes {
+	//                  whichME     := unknown.EntityClass			// ClassID
+	//                  whichInst   := unknown.EntityInstance		// uint16
+	//					unknownMask := unknown.AttributeMask		// uint16
+	//					unknownBlob := unknown.Attributes			// []byte
+	//
+	//                  // Unless this is the extended message set and only a single attribute
+	//              	// mask bit is set, you really do not know what possible kind of data
+	//              	// type the attribute is...
+	//               }
+	//	         }
+	//       }
+	/////////////////////////////////////
+	assert.NotEqual(t, gopacket.LayerTypePayload, response.NextLayerType())
+	assert.Equal(t, LayerTypeUnknownAttributes, response.NextLayerType())
+
+	unknownAttrLayer := packet.Layer(LayerTypeUnknownAttributes)
+	assert.NotNil(t, unknownAttrLayer)
+
+	unknown, ok2 := unknownAttrLayer.(*UnknownAttributes)
+	assert.True(t, ok2)
+	assert.NotNil(t, unknown)
+	assert.Equal(t, LayerTypeUnknownAttributes, unknown.LayerType())
+	assert.Equal(t, LayerTypeUnknownAttributes, unknown.CanDecode())
+	assert.Equal(t, gopacket.LayerTypeZero, unknown.NextLayerType())
+
+	// Only one Managed entity was in this response and had a bad attribute
+	assert.Equal(t, 1, len(unknown.Attributes))
+	assert.Equal(t, me.OltGClassID, unknown.Attributes[0].EntityClass)
+	assert.Equal(t, uint16(0), unknown.Attributes[0].EntityInstance)
+	assert.Equal(t, uint16(0x0c00), unknown.Attributes[0].AttributeMask)
+	assert.Equal(t, uint16(0x0c00), unknown.Attributes[0].AttributeMask)
+	assert.Equal(t, extraTrailerData, unknown.Attributes[0].AttributeData)
+
+}
+
+// TestMibUploadNextResponseExtendedRelaxedDecode is the extended message
+// set test of the test above (with two Managed Entity where both have bad attributes)
+func TestMibUploadNextResponseExtendedRelaxedDecodeTwoMEs(t *testing.T) {
+	// Test msg has OLT-G ME that normally only has 4 attributes defined. Since several are
+	// pretty big and would normally take at least 3 MIB upload next frames.  So in
+	// this one it has the last 'known' one, plus two new ones.
+	extraTrailer1 := "123400001234"                 // 6 octets
+	extraTrailer2 := "432100004321"                 // 6 octets
+	goodAttribute := "123456780a090807060504030201" // 14 octets
+	mibUploadNextLayer := "00020000" + "0038" +
+		"0014" +
+		"008300001c00" + // 6 octets
+		goodAttribute + extraTrailer1 + // 14 + 6 octets
+		"0014" +
+		"008300011c00" + // 6 octets	(entity ID 1 which is invalid for OLT-g, but this is a test)
+		goodAttribute + extraTrailer2 // 14 + 6 octets
+
+	goodMessage := "02862e0b" + mibUploadNextLayer
+
+	data, err := stringToPacket(goodMessage)
+	assert.NoError(t, err)
+
+	extraTrailerData1, _ := stringToPacket(extraTrailer1)
+	assert.NotNil(t, extraTrailerData1)
+	extraTrailerData2, _ := stringToPacket(extraTrailer2)
+	assert.NotNil(t, extraTrailerData2)
+
+	mibUploadNextLayerData, _ := stringToPacket(mibUploadNextLayer)
+	assert.NotNil(t, mibUploadNextLayerData)
+
+	goodAttributeData, _ := stringToPacket(goodAttribute)
+	assert.NotNil(t, goodAttributeData)
+
+	// Make sure relaxed decode is disabled
+	assert.NoError(t, me.SetRelaxedDecode(me.MibUploadNext, false, false))
+	assert.False(t, me.GetRelaxedDecode(me.MibUploadNext, false))
+
+	// Should get a packet but there should also be an error layer after the OMCI layer
+	packet := gopacket.NewPacket(data, LayerTypeOMCI, gopacket.NoCopy)
+	assert.NotNil(t, packet)
+
+	omciLayer := packet.Layer(LayerTypeOMCI)
+	assert.NotNil(t, omciLayer)
+
+	omciMsg, ok := omciLayer.(*OMCI)
+	assert.True(t, ok)
+	assert.NotNil(t, omciMsg)
+
+	assert.Equal(t, LayerTypeOMCI, omciMsg.LayerType())
+	assert.Equal(t, LayerTypeOMCI, omciMsg.CanDecode())
+	assert.Equal(t, LayerTypeMibUploadNextResponse, omciMsg.NextLayerType())
+	assert.Equal(t, uint16(0x0286), omciMsg.TransactionID)
+	assert.Equal(t, MibUploadNextResponseType, omciMsg.MessageType)
+	assert.Equal(t, ExtendedIdent, omciMsg.DeviceIdentifier)
+	assert.Equal(t, uint16(56), omciMsg.Length)
+
+	// Without relaxed decode, the MIB Upload Next Response cannot be decoded further
+	// but can get the error layer and it's contents (which is the entire MIB Upload Response data
+	msgLayer := packet.Layer(LayerTypeMibUploadNextResponse)
+	assert.Nil(t, msgLayer)
+
+	errLayer := packet.Layer(gopacket.LayerTypeDecodeFailure)
+	assert.NotNil(t, errLayer)
+	assert.Nil(t, errLayer.LayerPayload())
+	errContents := errLayer.LayerContents()
+	assert.NotNil(t, errContents)
+	assert.Equal(t, mibUploadNextLayerData, errContents)
+
+	////////////////////////////////////////////////////////////////////////////
+	////////////////////////////////////////////////////////////////////////////
+	////////////////////////////////////////////////////////////////////////////
+	// Now turn on relaxed decode and you can now go further into the packet
+	assert.NoError(t, me.SetRelaxedDecode(me.MibUploadNext, false, true))
+	assert.True(t, me.GetRelaxedDecode(me.MibUploadNext, false))
+	packet = gopacket.NewPacket(data, LayerTypeOMCI, gopacket.NoCopy)
+	assert.NotNil(t, packet)
+
+	omciLayer = packet.Layer(LayerTypeOMCI)
+	assert.NotNil(t, omciLayer)
+
+	omciMsg, ok = omciLayer.(*OMCI)
+	assert.True(t, ok)
+	assert.NotNil(t, omciMsg)
+
+	// Skipping the test of OMCI layer values. It is same as above
+	//
+	// Get that message layer that has data that could be decoded
+	msgLayer = packet.Layer(LayerTypeMibUploadNextResponse)
+	assert.NotNil(t, msgLayer)
+
+	response, ok2 := msgLayer.(*MibUploadNextResponse)
+	assert.True(t, ok2)
+	assert.NotNil(t, response)
+	assert.Equal(t, LayerTypeMibUploadNextResponse, response.LayerType())
+	assert.Equal(t, LayerTypeMibUploadNextResponse, response.CanDecode())
+	assert.Equal(t, me.OltGClassID, response.ReportedME.GetClassID())
+	assert.Equal(t, uint16(0), response.ReportedME.GetEntityID())
+
+	// The attribute mask decoded at this layer only contains the attributes we
+	// could successfully decode
+	assert.Equal(t, uint16(0x1000), response.ReportedME.GetAttributeMask())
+
+	attributes := me.AttributeValueMap{
+		"TimeOfDayInformation": goodAttributeData, // NOTE: This is binary data for the comparison below
+	}
+	for name, value := range attributes {
+		pktValue, err := response.ReportedME.GetAttribute(name)
+		assert.Nil(t, err)
+		assert.Equal(t, pktValue, value)
+	}
+	// Now the second ME in the response
+	assert.NotNil(t, response.AdditionalMEs)
+	assert.Equal(t, 1, len(response.AdditionalMEs))
+
+	////////////////////////////////////////////////////////////////////////////
+	// Here is the new functionality.  In order to store both a well decoded
+	// MIB UPLOAD NEXT response layer, along with a relaxed decode error, the
+	// layer addition has to be done in a specific way and an error returned.
+	//
+	//     Note that the previous line (below) worked in the code above
+	//
+	//           response, ok2 := msgLayer.(*MibUploadNextResponse)
+	//
+	// If you did not care about what the relaxed decode found out, just proceed
+	// on as normal.  However, since you enabled relaxed decoding of the unknown
+	// attributes, here is where you can pull extra information from.
+	//
+	//  The first way is to just try and see if that error layer was decoded
+	//
+	//      if unknownAttrLayer = packet.Layer(LayerTypeUnknownAttributes); unknownAttrLayer != nil {
+	//          log.warning(HEY! Got some unknown attributes to this ME: '%v', unknownAttrLayer)
+	//
+	//          unknownAttributes, ok2 := msgLayer.(*UnknownAttributes); ok {
+	//				//
+	//				// Since some extended messages can actually return multiple managed entities,
+	//              // all ME's with unknown attributes need to be uniquely identified
+	//              //
+	//              for _, unknown := range unknownAttibutes.Attributes {
+	//                  whichME     := unknown.EntityClass			// ClassID
+	//                  whichInst   := unknown.EntityInstance		// uint16
+	//					unknownMask := unknown.AttributeMask		// uint16
+	//					unknownBlob := unknown.Attributes			// []byte
+	//
+	//                  // Unless this is the extended message set and only a single attribute
+	//              	// mask bit is set, you really do not know what possible kind of data
+	//              	// type the attribute is...
+	//               }
+	//	         }
+	//       }
+	/////////////////////////////////////
+	assert.NotEqual(t, gopacket.LayerTypePayload, response.NextLayerType())
+	assert.Equal(t, LayerTypeUnknownAttributes, response.NextLayerType())
+
+	unknownAttrLayer := packet.Layer(LayerTypeUnknownAttributes)
+	assert.NotNil(t, unknownAttrLayer)
+
+	unknown, ok2 := unknownAttrLayer.(*UnknownAttributes)
+	assert.True(t, ok2)
+	assert.NotNil(t, unknown)
+	assert.Equal(t, LayerTypeUnknownAttributes, unknown.LayerType())
+	assert.Equal(t, LayerTypeUnknownAttributes, unknown.CanDecode())
+	assert.Equal(t, gopacket.LayerTypeZero, unknown.NextLayerType())
+
+	// Only one Managed entity was in this response and had a bad attribute
+	assert.Equal(t, 2, len(unknown.Attributes))
+	assert.Equal(t, me.OltGClassID, unknown.Attributes[0].EntityClass)
+	assert.Equal(t, uint16(0), unknown.Attributes[0].EntityInstance)
+	assert.Equal(t, uint16(0x0c00), unknown.Attributes[0].AttributeMask)
+	assert.Equal(t, extraTrailerData1, unknown.Attributes[0].AttributeData)
+
+	assert.Equal(t, me.OltGClassID, unknown.Attributes[1].EntityClass)
+	assert.Equal(t, uint16(1), unknown.Attributes[1].EntityInstance)
+	assert.Equal(t, uint16(0x0c00), unknown.Attributes[1].AttributeMask)
+	assert.Equal(t, extraTrailerData2, unknown.Attributes[1].AttributeData)
+
+	errStr := unknown.Error()
+	assert.NotNil(t, errStr)
+	assert.Greater(t, len(errStr.Error()), 0)
+}