VOL-4338: Relaxed MIB Upload Next response decoding

Change-Id: I4c0db4d4786a1d8501daec18a6980821a9267b84
diff --git a/generated/decodeerror.go b/generated/decodeerror.go
new file mode 100644
index 0000000..4358c0c
--- /dev/null
+++ b/generated/decodeerror.go
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+/*
+ * NOTE: This file was generated, manual edits will be overwritten!
+ *
+ * Generated by 'goCodeGenerator.py':
+ *              https://github.com/cboling/OMCI-parser/README.md
+ */
+
+package generated
+
+import (
+	"errors"
+)
+
+// Custom Go Error messages for relaxed decode error signaling.
+//
+//   gopacket does not provide a way to easily perform relaxed decoding calls
+//   during the 'DecodeFromBytes' decoding of a layer. It does allow for an error
+//   code returned and this will be used to allow for relaxed decoding. If a
+//   particular message type decode can be relaxed, process it as relax and
+//   return an error derived from the RelaxedDecodeError below and check for
+//   it as appropriate.
+
+// IRelaxedDecodeError provides a base interface that can be used to derive
+// other decode specific errors that can be relaxed at the application's
+// discretion
+type IRelaxedDecodeError interface {
+	// Error interface, so if relaxed decode is not supported, this behaves as
+	// a normal error
+	Error() string
+	GetError() error
+
+	////////////////////////////////
+	// Relaxed error specific
+
+	// GetContents returns the octet payload specific to the error if it can be
+	// determined. Derived relaxed decode errors may provide more specific control
+	// and information
+	GetContents() []byte
+}
+
+type RelaxedDecodeError struct {
+	err      string
+	Contents []byte
+}
+
+func (e *RelaxedDecodeError) GetError() error {
+	return errors.New(e.err)
+}
+
+func (e *RelaxedDecodeError) Error() string {
+	return e.err
+}
+
+func (e *RelaxedDecodeError) GetContents() []byte {
+	return e.Contents
+}
+
+// UnknownAttributeDecodeError is used to convey unknown attributes found in
+// a received packet undergoing decode. These will always be trailing attributes
+// in packets and are often due to:
+//     o New versions of ITU G.988 being issued with an existing ME getting new
+//       attributes,
+//
+//     o An error in the code generated classes in this library where one or more
+//       attributes were missed, or
+//
+//     o An error in the OLT/ONU that serialized the message
+type UnknownAttributeDecodeError struct {
+	RelaxedDecodeError
+	AttributeMask uint16
+
+	EntityClass    ClassID // Set by first level handler of the error
+	EntityInstance uint16  // Set by first level handler of the error
+}
+
+func NewUnknownAttributeDecodeError(msg string, mask uint16, contents []byte) *UnknownAttributeDecodeError {
+	err := &UnknownAttributeDecodeError{
+		RelaxedDecodeError: RelaxedDecodeError{
+			err: msg,
+		},
+		AttributeMask: mask,
+	}
+	if contents != nil {
+		err.Contents = make([]byte, len(contents))
+		copy(err.Contents, contents)
+	}
+	return err
+}
diff --git a/generated/me.go b/generated/me.go
index 8bec058..2d83255 100644
--- a/generated/me.go
+++ b/generated/me.go
@@ -286,15 +286,26 @@
 	entity.definition = meDefinition.definition
 	entity.attributeMask = binary.BigEndian.Uint16(data[4:6])
 	entity.attributes = make(map[string]interface{})
-	entity.SetEntityID(entityID)
+	setErr := entity.SetEntityID(entityID)
+	if setErr != nil {
+		return setErr
+	}
 	packetAttributes, err := entity.DecodeAttributes(entity.GetAttributeMask(), data[6:], p, msgType)
+
+	// Decode packet attributes even if present in case relaxed attribute decoding is enabled.
+	if packetAttributes != nil {
+		for name, value := range packetAttributes {
+			entity.attributes[name] = value
+		}
+	}
 	if err != nil {
-		return err
+		if attrError, ok := err.(*UnknownAttributeDecodeError); ok && GetRelaxedDecodeByOctetType(msgType) {
+			// Subtract off bad mask from what we computed
+			badMask := attrError.AttributeMask
+			entity.attributeMask &= ^badMask
+		}
 	}
-	for name, value := range packetAttributes {
-		entity.attributes[name] = value
-	}
-	return nil
+	return err
 }
 
 // SerializeTo serializes a Managed Entity into an octet stream
diff --git a/generated/medef.go b/generated/medef.go
index d6ab352..6d2c2dd 100644
--- a/generated/medef.go
+++ b/generated/medef.go
@@ -90,10 +90,15 @@
 }
 
 func (bme ManagedEntityDefinition) DecodeAttributes(mask uint16, data []byte, p gopacket.PacketBuilder, msgType byte) (AttributeValueMap, error) {
-	if (mask | bme.GetAllowedAttributeMask()) != bme.GetAllowedAttributeMask() {
-		return nil, fmt.Errorf("unsupported attribute mask %#x, valid: %#x for ME %v (Class ID: %d)",
+	badMask := (mask | bme.GetAllowedAttributeMask()) ^ bme.GetAllowedAttributeMask()
+
+	var maskErr error
+	if badMask != 0 {
+		maskErr = fmt.Errorf("unsupported attribute mask %#x, valid: %#x for ME %v (Class ID: %d)",
 			mask, bme.GetAllowedAttributeMask(), bme.GetName(), bme.ClassID)
+		mask &= bme.GetAllowedAttributeMask()
 	}
+	// Process known attributes
 	keyList := GetAttributeDefinitionMapKeys(bme.AttributeDefinitions)
 
 	attrMap := make(AttributeValueMap, bits.OnesCount16(mask))
@@ -153,7 +158,13 @@
 			}
 		}
 	}
-	return attrMap, nil
+	// If badMask is non-zero.  Handle it by re-encoding the error as a custom relaxed
+	// decode error that the caller of this decode can process if they wish to relax
+	// the decoding
+	if badMask != 0 {
+		maskErr = NewUnknownAttributeDecodeError(maskErr.Error(), badMask, data)
+	}
+	return attrMap, maskErr
 }
 
 func (bme ManagedEntityDefinition) SerializeAttributes(attr AttributeValueMap, mask uint16,
diff --git a/generated/relaxed.go b/generated/relaxed.go
new file mode 100644
index 0000000..a1e49d3
--- /dev/null
+++ b/generated/relaxed.go
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+/*
+ * NOTE: This file was generated, manual edits will be overwritten!
+ *
+ * Generated by 'goCodeGenerator.py':
+ *              https://github.com/cboling/OMCI-parser/README.md
+ */
+package generated
+
+import (
+	"fmt"
+)
+
+var relaxedDecode map[int]bool
+
+func init() {
+	relaxedDecode = make(map[int]bool)
+
+	// Default for relaxed decode is True if there is relaxed decode support available
+	// relaxedDecode[mkRelaxedKey(Get, false)] = true
+	relaxedDecode[mkRelaxedKey(MibUploadNext, false)] = true
+}
+
+func mkRelaxedKey(msgType MsgType, request bool) int {
+	if request {
+		return int(msgType)
+	}
+	return 100 + int(msgType)
+}
+
+func SetRelaxedDecode(msgType MsgType, request bool, relax bool) error {
+	key := mkRelaxedKey(msgType, request)
+
+	if _, ok := relaxedDecode[key]; !ok {
+		return fmt.Errorf("relax decode of '%v' is not supported", msgType)
+	}
+	relaxedDecode[key] = relax
+	return nil
+}
+
+func GetRelaxedDecode(msgType MsgType, request bool) bool {
+	key := mkRelaxedKey(msgType, request)
+
+	relaxed, ok := relaxedDecode[key]
+	return ok && relaxed
+}
+
+// GetRelaxedDecodeByOctetType decodes the payload message-type value and determine if
+// relaxed decode is enabled
+func GetRelaxedDecodeByOctetType(value byte) bool {
+	msgType := MsgType(value & MsgTypeMask)
+	var request bool
+
+	if msgType != AlarmNotification && msgType != AttributeValueChange && msgType != TestResult {
+		request = value&AR == AR || value&AK == 0
+	}
+	return GetRelaxedDecode(msgType, request)
+}