VOL-4338: Relaxed MIB Upload Next response decoding

Change-Id: I4c0db4d4786a1d8501daec18a6980821a9267b84
diff --git a/mibupload.go b/mibupload.go
index dd893d4..a8549a1 100644
--- a/mibupload.go
+++ b/mibupload.go
@@ -353,6 +353,8 @@
 	MeBasePacket
 	ReportedME    me.ManagedEntity
 	AdditionalMEs []me.ManagedEntity // Valid only for extended message set version
+
+	RelaxedErrors []me.IRelaxedDecodeError
 }
 
 type MibUploadNextManageEntity struct {
@@ -377,9 +379,21 @@
 
 // NextLayerType returns the layer type contained by this DecodingLayer.
 func (omci *MibUploadNextResponse) NextLayerType() gopacket.LayerType {
+
+	if omci.RelaxedErrors != nil && len(omci.RelaxedErrors) > 0 {
+		return LayerTypeUnknownAttributes
+	}
 	return gopacket.LayerTypePayload
 }
 
+// addRelaxedError appends relaxed decode errors to this message
+func (omci *MibUploadNextResponse) addRelaxedError(err me.IRelaxedDecodeError) {
+	if omci.RelaxedErrors == nil {
+		omci.RelaxedErrors = make([]me.IRelaxedDecodeError, 0)
+	}
+	omci.RelaxedErrors = append(omci.RelaxedErrors, err)
+}
+
 // DecodeFromBytes decodes the given bytes of a MIB Upload Next Response into this layer
 func (omci *MibUploadNextResponse) DecodeFromBytes(data []byte, p gopacket.PacketBuilder) error {
 	// Common ClassID/EntityID decode in msgBase
@@ -418,47 +432,77 @@
 	// error of "managed entity definition not found" returned.
 	var offset int
 	var attrLen int
+	meLength := len(data)
 	if omci.Extended {
 		offset = 2 + 2 // Message Contents length (2) + first ME attribute values len (2)
 		attrLen = int(binary.BigEndian.Uint16(data[6:]))
+		meLength = 4 + offset + 6 + attrLen
 
 		if len(data[4+offset:]) < 6+attrLen {
 			p.SetTruncated()
 			return errors.New("frame too small: MIB Upload Response Managed Entity attribute truncated")
 		}
 	}
-	err = omci.ReportedME.DecodeFromBytes(data[4+offset:], p, byte(MibUploadNextResponseType))
-	if err != nil || !omci.Extended {
-		return err
+	err = omci.ReportedME.DecodeFromBytes(data[4+offset:meLength], p, byte(MibUploadNextResponseType))
+	if err != nil {
+		attrError, ok := err.(*me.UnknownAttributeDecodeError)
+
+		// Error if relaxed decode not supported or other error signalled
+		if !ok || !me.GetRelaxedDecode(me.MibUploadNext, false) {
+			return err
+		}
+		// Save off which Managed Entity had the issue
+		attrError.EntityClass = omci.ReportedME.GetClassID()
+		attrError.EntityInstance = omci.ReportedME.GetEntityID()
+		if attrError.Contents != nil && !omci.Extended {
+			attrLen += len(attrError.Contents)
+		}
+		omci.addRelaxedError(attrError)
+		err = nil
 	}
-	// Handle extended message set decode here for additional attributes
-	remaining := len(data) - (6 + 8 + attrLen)
-	if remaining > 0 {
-		offset = 6 + 8 + attrLen
-		omci.AdditionalMEs = make([]me.ManagedEntity, 0)
-		for remaining > 0 {
-			if len(data[offset:]) < 8 {
+	if err == nil && omci.Extended {
+		// Handle extended message set decode here for additional managed entities
+		data = data[meLength:]
+		if len(data) > 0 {
+			omci.AdditionalMEs = make([]me.ManagedEntity, 0)
+		}
+		for len(data) > 0 {
+			if len(data) < 8 {
 				p.SetTruncated()
 				// TODO: Review all "frame to small" and add an extra hint for developers
 				return errors.New("frame too small: MIB Upload Response Managed Entity header truncated")
 			}
 			additional := me.ManagedEntity{}
-			attrLen = int(binary.BigEndian.Uint16(data[offset:]))
+			attrLen = int(binary.BigEndian.Uint16(data))
+			meLength = 8 + attrLen
 
-			if len(data[offset:]) < 8+attrLen {
+			if len(data) < meLength {
 				p.SetTruncated()
 				return errors.New("frame too small: MIB Upload Response Managed Entity attribute truncated")
 			}
-			err = additional.DecodeFromBytes(data[offset+2:], p, byte(MibUploadNextResponseType))
+			err = additional.DecodeFromBytes(data[2:meLength], p, byte(MibUploadNextResponseType))
 			if err != nil {
-				return err
+				attrError, ok := err.(*me.UnknownAttributeDecodeError)
+
+				// Error if relaxed decode not supported
+				if !ok || !me.GetRelaxedDecode(me.MibUploadNext, false) {
+					return err
+				}
+				// Save off which Managed Entity had the issue
+				attrError.EntityClass = additional.GetClassID()
+				attrError.EntityInstance = additional.GetEntityID()
+				omci.addRelaxedError(attrError)
+				err = nil
 			}
 			omci.AdditionalMEs = append(omci.AdditionalMEs, additional)
-			remaining -= 8 + attrLen
-			offset += 8 + attrLen
+			data = data[meLength:]
 		}
 	}
-	return nil
+	if err == nil && omci.RelaxedErrors != nil && len(omci.RelaxedErrors) > 0 {
+		// Create our error layer now
+		err = newUnknownAttributesLayer(omci, omci.RelaxedErrors, p)
+	}
+	return err
 }
 
 func decodeMibUploadNextResponse(data []byte, p gopacket.PacketBuilder) error {