VOL-3984: OMCI-LIB-GO: Support for Extended message set for DownloadSection Request/Response type
Change-Id: Ieb421aabe145a059baa7b015f8ab0fd2b52d2a14
diff --git a/VERSION b/VERSION
index 9084fa2..26aaba0 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.1.0
+1.2.0
diff --git a/layers.go b/layers.go
index aa49e8c..4a527bf 100644
--- a/layers.go
+++ b/layers.go
@@ -50,7 +50,9 @@
LayerTypeSetTableRequest gopacket.LayerType
// Extended Message Types
- LayerTypeGetRequestExtended gopacket.LayerType
+ LayerTypeGetRequestExtended gopacket.LayerType
+ LayerTypeDownloadSectionRequestExtended gopacket.LayerType
+ LayerTypeDownloadSectionLastRequestExtended gopacket.LayerType
)
var (
// Baseline Message Types
@@ -79,7 +81,8 @@
LayerTypeSetTableResponse gopacket.LayerType
// Extended Message Types
- LayerTypeGetResponseExtended gopacket.LayerType
+ LayerTypeGetResponseExtended gopacket.LayerType
+ LayerTypeDownloadSectionResponseExtended gopacket.LayerType
)
func mkReqLayer(mt me.MsgType, mts string, decode gopacket.DecodeFunc) gopacket.LayerType {
@@ -151,6 +154,9 @@
LayerTypeGetRequestExtended = mkReqLayer(me.Get|me.ExtendedOffset, "GetRequest-Ext", gopacket.DecodeFunc(decodeGetRequestExtended))
LayerTypeGetResponseExtended = mkRespLayer(me.Get|me.ExtendedOffset, "GetResponse-Ext", gopacket.DecodeFunc(decodeGetResponseExtended))
+ LayerTypeDownloadSectionRequestExtended = mkLayer(me.DownloadSection|me.ExtendedOffset, "DownloadSectionRequest-Ext", gopacket.DecodeFunc(decodeDownloadSectionRequestExtended))
+ LayerTypeDownloadSectionLastRequestExtended = mkReqLayer(me.DownloadSection|me.ExtendedOffset, "DownloadLastSectionRequest-Ext", gopacket.DecodeFunc(decodeDownloadSectionRequestExtended))
+ LayerTypeDownloadSectionResponseExtended = mkRespLayer(me.DownloadSection|me.ExtendedOffset, "DownloadSectionResponse-Ext", gopacket.DecodeFunc(decodeDownloadSectionResponseExtended))
// Map message_type and action to layer
nextLayerMapping = make(map[MessageType]gopacket.LayerType)
@@ -205,6 +211,11 @@
// Extended message support
nextLayerMapping[GetRequestType+ExtendedTypeDecodeOffset] = LayerTypeGetRequestExtended
nextLayerMapping[GetResponseType+ExtendedTypeDecodeOffset] = LayerTypeGetResponseExtended
+
+ // For Download section, AR=0 if not response expected, AR=1 if response expected (last section of a window)
+ nextLayerMapping[DownloadSectionRequestType+ExtendedTypeDecodeOffset] = LayerTypeDownloadSectionRequestExtended
+ nextLayerMapping[DownloadSectionRequestWithResponseType+ExtendedTypeDecodeOffset] = LayerTypeDownloadSectionLastRequestExtended
+ nextLayerMapping[DownloadSectionResponseType+ExtendedTypeDecodeOffset] = LayerTypeDownloadSectionResponseExtended
}
func MsgTypeToNextLayer(mt MessageType, isExtended bool) (gopacket.LayerType, error) {
diff --git a/meframe.go b/meframe.go
index c13554c..472db31 100644
--- a/meframe.go
+++ b/meframe.go
@@ -283,17 +283,21 @@
// Software related frames have a wide variety of settable values. Placing them
// in a separate struct is mainly to keep the base options simple
type SoftwareOptions struct {
- WindowSize uint8 // Window size - 1
- ImageSize uint32
- CircuitPacks []uint16 // slot (upper 8 bits) and instance (lower 8 bits)
- Results []DownloadResults
+ WindowSize uint8 // Window size - 1
+ SectionNumber uint8 // [0..Window size - 1]
+ ImageSize uint32
+ CircuitPacks []uint16 // slot (upper 8 bits) and instance (lower 8 bits)
+ Results []DownloadResults
+ Data []byte
}
var defaultSoftwareOptions = SoftwareOptions{
- WindowSize: 0,
- ImageSize: 0,
- CircuitPacks: nil,
- Results: nil,
+ WindowSize: 0,
+ SectionNumber: 0,
+ ImageSize: 0,
+ CircuitPacks: nil,
+ Results: nil,
+ Data: nil,
}
// EncodeFrame will encode the Managed Entity specific protocol struct and an
@@ -364,9 +368,9 @@
}
// OMCI Header - 4 octets
// Class ID/Instance ID - 4 octets
- // Length field - 4 octets
+ // Length field - 2 octets
// MIC - 4 octets
- return MaxExtendedLength - 16
+ return MaxExtendedLength - 14
}
func calculateAttributeMask(m *me.ManagedEntity, requestedMask uint16) (uint16, error) {
@@ -1040,12 +1044,8 @@
}
func DownloadSectionRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
- if opt.frameFormat == ExtendedIdent {
- return nil, errors.New("Extended message set for this message type is not supported")
- }
- mask, err := checkAttributeMask(m, opt.attributeMask)
- if err != nil {
- return nil, err
+ if opt.software.Data == nil {
+ return nil, me.NewNonStatusError("Software image data missing")
}
// Common for all MEs
meLayer := &DownloadSectionRequest{
@@ -1054,24 +1054,13 @@
EntityInstance: m.GetEntityID(),
Extended: opt.frameFormat == ExtendedIdent,
},
+ SectionNumber: opt.software.SectionNumber,
+ SectionData: opt.software.Data,
}
- // Get payload space available
- maxPayload := maxPacketAvailable(m, opt)
-
- // TODO: Lots of work to do
-
- fmt.Println(mask, maxPayload)
- return meLayer, errors.New("todo: Not implemented")
+ return meLayer, nil
}
func DownloadSectionResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
- if opt.frameFormat == ExtendedIdent {
- return nil, errors.New("Extended message set for this message type is not supported")
- }
- mask, err := checkAttributeMask(m, opt.attributeMask)
- if err != nil {
- return nil, err
- }
// Common for all MEs
meLayer := &DownloadSectionResponse{
MeBasePacket: MeBasePacket{
@@ -1079,14 +1068,10 @@
EntityInstance: m.GetEntityID(),
Extended: opt.frameFormat == ExtendedIdent,
},
+ Result: opt.result,
+ SectionNumber: opt.software.SectionNumber,
}
- // Get payload space available
- maxPayload := maxPacketAvailable(m, opt)
-
- // TODO: Lots of work to do
-
- fmt.Println(mask, maxPayload)
- return meLayer, errors.New("todo: Not implemented")
+ return meLayer, nil
}
func EndSoftwareDownloadRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
diff --git a/meframe_test.go b/meframe_test.go
index cf42492..fd9d339 100644
--- a/meframe_test.go
+++ b/meframe_test.go
@@ -52,6 +52,8 @@
messageTypeTestFuncs[MibResetResponseType] = testMibResetResponseTypeMeFrame
messageTypeTestFuncs[TestRequestType] = testTestRequestTypeMeFrame
messageTypeTestFuncs[TestResponseType] = testTestResponseTypeMeFrame
+
+ // For Download section, AR=0 if not response expected, AR=1 if response expected (last section of a window)4
messageTypeTestFuncs[StartSoftwareDownloadRequestType] = testStartSoftwareDownloadRequestTypeMeFrame
messageTypeTestFuncs[StartSoftwareDownloadResponseType] = testStartSoftwareDownloadResponseTypeMeFrame
messageTypeTestFuncs[DownloadSectionRequestType] = testDownloadSectionRequestTypeMeFrame
@@ -79,6 +81,11 @@
// Supported Extended message set types here
messageTypeTestFuncs[GetRequestType+ExtendedTypeDecodeOffset] = testGetRequestTypeMeFrame
messageTypeTestFuncs[GetResponseType+ExtendedTypeDecodeOffset] = testGetResponseTypeMeFrame
+
+ // For Download section, AR=0 if not response expected, AR=1 if response expected (last section of a window)
+ messageTypeTestFuncs[DownloadSectionRequestType+ExtendedTypeDecodeOffset] = testDownloadSectionRequestTypeMeFrame
+ // TODO: messageTypeTestFuncs[DownloadSectionRequestWithResponseType+ExtendedTypeDecodeOffset] = testDownloadSectionLastRequestTypeMeFrame
+ messageTypeTestFuncs[DownloadSectionResponseType+ExtendedTypeDecodeOffset] = testDownloadSectionResponseTypeMeFrame
}
func getMEsThatSupportAMessageType(messageType MessageType) []*me.ManagedEntity {
@@ -1460,11 +1467,117 @@
}
func testDownloadSectionRequestTypeMeFrame(t *testing.T, managedEntity *me.ManagedEntity, messageSet DeviceIdent) {
- // TODO: Implement
+ // TODO: In future, also support slot/multiple download formats
+ instance := uint16(0)
+ image := uint16(rand.Intn(1)) // Image 0 or 1 for this test
+ params := me.ParamData{
+ EntityID: (instance << 8) + image,
+ }
+ // Create the managed instance
+ meInstance, err := me.NewManagedEntity(managedEntity.GetManagedEntityDefinition(), params)
+ assert.NotNil(t, err)
+ assert.Equal(t, err.StatusCode(), me.Success)
+
+ tid := uint16(rand.Int31n(0xFFFE) + 1) // [1, 0xFFFF]
+ var data []byte
+
+ if messageSet == ExtendedIdent {
+ data = make([]byte, MaxDownloadSectionExtendedLength)
+ } else {
+ data = make([]byte, MaxDownloadSectionLength)
+ }
+ for index, _ := range data {
+ data[index] = byte(index & 0xFF)
+ }
+ options := SoftwareOptions{
+ SectionNumber: uint8(rand.Int31n(255)), // [0, 255]
+ Data: data,
+ }
+ frame, omciErr := GenFrame(meInstance, DownloadSectionRequestType,
+ TransactionID(tid), Software(options), FrameFormat(messageSet))
+ assert.NotNil(t, frame)
+ assert.NotZero(t, len(frame))
+ assert.Nil(t, omciErr)
+
+ ///////////////////////////////////////////////////////////////////
+ // Now decode and compare
+ packet := gopacket.NewPacket(frame, LayerTypeOMCI, gopacket.NoCopy)
+ assert.NotNil(t, packet)
+
+ omciLayer := packet.Layer(LayerTypeOMCI)
+ assert.NotNil(t, omciLayer)
+
+ omciObj, omciOk := omciLayer.(*OMCI)
+ assert.NotNil(t, omciObj)
+ assert.True(t, omciOk)
+ assert.Equal(t, tid, omciObj.TransactionID)
+ assert.Equal(t, DownloadSectionRequestType, omciObj.MessageType)
+ assert.Equal(t, messageSet, omciObj.DeviceIdentifier)
+
+ msgLayer := packet.Layer(LayerTypeDownloadSectionRequest)
+ assert.NotNil(t, msgLayer)
+
+ msgObj, msgOk := msgLayer.(*DownloadSectionRequest)
+ assert.NotNil(t, msgObj)
+ assert.True(t, msgOk)
+
+ assert.Equal(t, meInstance.GetClassID(), msgObj.EntityClass)
+ assert.Equal(t, meInstance.GetEntityID(), msgObj.EntityInstance)
+ assert.Equal(t, options.SectionNumber, msgObj.SectionNumber)
+ assert.NotNil(t, msgObj.SectionData)
+ assert.Equal(t, options.Data, msgObj.SectionData)
}
func testDownloadSectionResponseTypeMeFrame(t *testing.T, managedEntity *me.ManagedEntity, messageSet DeviceIdent) {
- // TODO: Implement
+ instance := uint16(0)
+ image := uint16(rand.Intn(1)) // Image 0 or 1 for this test
+ params := me.ParamData{
+ EntityID: (instance << 8) + image,
+ }
+ // Create the managed instance
+ meInstance, err := me.NewManagedEntity(managedEntity.GetManagedEntityDefinition(), params)
+ assert.NotNil(t, err)
+ assert.Equal(t, err.StatusCode(), me.Success)
+
+ tid := uint16(rand.Int31n(0xFFFE) + 1) // [1, 0xFFFF]
+ result := me.Results(rand.Int31n(7)) // [0, 6] Not all types will be tested
+ swOptions := SoftwareOptions{
+ SectionNumber: uint8(rand.Int31n(255)), // [0, 255]
+ }
+ var frame []byte
+ frame, omciErr := GenFrame(meInstance, DownloadSectionResponseType, TransactionID(tid),
+ Result(result), FrameFormat(messageSet), Software(swOptions))
+
+ assert.NotNil(t, frame)
+ assert.NotZero(t, len(frame))
+ assert.Nil(t, omciErr)
+
+ ///////////////////////////////////////////////////////////////////
+ // Now decode and compare
+ packet := gopacket.NewPacket(frame, LayerTypeOMCI, gopacket.NoCopy)
+ assert.NotNil(t, packet)
+
+ omciLayer := packet.Layer(LayerTypeOMCI)
+ assert.NotNil(t, omciLayer)
+
+ omciObj, omciOk := omciLayer.(*OMCI)
+ assert.NotNil(t, omciObj)
+ assert.True(t, omciOk)
+ assert.Equal(t, tid, omciObj.TransactionID)
+ assert.Equal(t, DownloadSectionResponseType, omciObj.MessageType)
+ assert.Equal(t, messageSet, omciObj.DeviceIdentifier)
+
+ msgLayer := packet.Layer(LayerTypeDownloadSectionResponse)
+ assert.NotNil(t, msgLayer)
+
+ msgObj, msgOk := msgLayer.(*DownloadSectionResponse)
+ assert.NotNil(t, msgObj)
+ assert.True(t, msgOk)
+
+ assert.Equal(t, meInstance.GetClassID(), msgObj.EntityClass)
+ assert.Equal(t, meInstance.GetEntityID(), msgObj.EntityInstance)
+ assert.Equal(t, result, msgObj.Result)
+ assert.Equal(t, swOptions.SectionNumber, msgObj.SectionNumber)
}
func testEndSoftwareDownloadRequestTypeMeFrame(t *testing.T, managedEntity *me.ManagedEntity, messageSet DeviceIdent) {
diff --git a/messagetypes.go b/messagetypes.go
index 456bef8..94fc257 100644
--- a/messagetypes.go
+++ b/messagetypes.go
@@ -2353,17 +2353,18 @@
return nil
}
-/////////////////////////////////////////////////////////////////////////////
-//
+// DownloadSectionRequest data is bound by the message set in use. For the
+// Baseline message set use MaxDownloadSectionLength and for the Extended message
+// set, MaxDownloadSectionExtendedLength is provided
type DownloadSectionRequest struct {
MeBasePacket // Note: EntityInstance for software download is two specific values
SectionNumber byte
- SectionData [31]byte // 0 padding if final transfer requires only a partial block
+ SectionData []byte // 0 padding if final transfer requires only a partial block for baseline set
}
func (omci *DownloadSectionRequest) String() string {
- return fmt.Sprintf("%v, Section #: %v",
- omci.MeBasePacket.String(), omci.SectionNumber)
+ return fmt.Sprintf("%v, Section #: %v, Data Length: %v",
+ omci.MeBasePacket.String(), omci.SectionNumber, len(omci.SectionData))
}
// DecodeFromBytes decodes the given bytes of a Download Section Request into this layer
@@ -2386,8 +2387,28 @@
if omci.EntityClass != me.SoftwareImageClassID {
return me.NewProcessingError("invalid Entity Class for Download Section request")
}
- omci.SectionNumber = data[4]
- copy(omci.SectionData[0:], data[5:])
+ if omci.Extended {
+ if len(data) < 7 {
+ p.SetTruncated()
+ return errors.New("frame too small")
+ }
+ if len(data[7:]) > MaxDownloadSectionExtendedLength {
+ return errors.New(fmt.Sprintf("software image data too large. Received %v, Max: %v",
+ len(data[7:]), MaxDownloadSectionExtendedLength))
+ }
+ omci.SectionData = make([]byte, len(data[7:]))
+ omci.SectionNumber = data[6]
+ copy(omci.SectionData, data[7:])
+ } else {
+ if len(data[5:]) != MaxDownloadSectionLength {
+ p.SetTruncated()
+ return errors.New(fmt.Sprintf("software image size invalid. Received %v, Expected: %v",
+ len(data[5:]), MaxDownloadSectionLength))
+ }
+ omci.SectionData = make([]byte, MaxDownloadSectionLength)
+ omci.SectionNumber = data[4]
+ copy(omci.SectionData, data[5:])
+ }
return nil
}
@@ -2397,6 +2418,13 @@
return decodingLayerDecoder(omci, data, p)
}
+func decodeDownloadSectionRequestExtended(data []byte, p gopacket.PacketBuilder) error {
+ omci := &DownloadSectionRequest{}
+ omci.MsgLayerType = LayerTypeDownloadSectionRequest
+ omci.Extended = true
+ return decodingLayerDecoder(omci, data, p)
+}
+
// SerializeTo provides serialization of an Download Section Request message
func (omci *DownloadSectionRequest) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
// Basic (common) OMCI Header is 8 octets, 10
@@ -2417,12 +2445,40 @@
if omci.EntityClass != me.SoftwareImageClassID {
return me.NewProcessingError("invalid Entity Class for Download Section response")
}
- bytes, err := b.AppendBytes(1 + len(omci.SectionData))
- if err != nil {
- return err
+ sectionLength := len(omci.SectionData)
+ if omci.Extended {
+ if sectionLength > MaxDownloadSectionExtendedLength {
+ msg := fmt.Sprintf("invalid Download Section data length, must be <= %v, received: %v",
+ MaxDownloadSectionExtendedLength, sectionLength)
+ return me.NewProcessingError(msg)
+ }
+ // Append section data
+ bytes, err := b.AppendBytes(3 + sectionLength)
+ if err != nil {
+ return err
+ }
+ binary.BigEndian.PutUint16(bytes, uint16(1+sectionLength))
+ bytes[2] = omci.SectionNumber
+ copy(bytes[3:], omci.SectionData)
+ } else {
+ if sectionLength > MaxDownloadSectionLength {
+ msg := fmt.Sprintf("invalid Download Section data length, must be <= %v, received: %v",
+ MaxDownloadSectionLength, sectionLength)
+ return me.NewProcessingError(msg)
+ }
+ // Append section data
+ bytes, err := b.AppendBytes(1 + MaxDownloadSectionLength)
+ if err != nil {
+ return err
+ }
+ bytes[0] = omci.SectionNumber
+ copy(bytes[1:], omci.SectionData)
+
+ // Zero extended if needed
+ if sectionLength < MaxDownloadSectionLength {
+ copy(omci.SectionData[sectionLength:], lotsOfZeros[:MaxDownloadSectionLength-sectionLength])
+ }
}
- bytes[0] = omci.SectionNumber
- copy(bytes[1:], omci.SectionData[0:])
return nil
}
@@ -2459,13 +2515,22 @@
if omci.EntityClass != me.SoftwareImageClassID {
return me.NewProcessingError("invalid Entity Class for Download Section response")
}
- omci.Result = me.Results(data[4])
+ if omci.Extended {
+ if len(data) < 8 {
+ p.SetTruncated()
+ return errors.New("frame too small")
+ }
+ omci.Result = me.Results(data[6])
+ omci.SectionNumber = data[7]
+ } else {
+ omci.Result = me.Results(data[4])
+ omci.SectionNumber = data[5]
+ }
if omci.Result > me.DeviceBusy {
msg := fmt.Sprintf("invalid results for Download Section response: %v, must be 0..6",
omci.Result)
return errors.New(msg)
}
- omci.SectionNumber = data[5]
return nil
}
@@ -2475,6 +2540,13 @@
return decodingLayerDecoder(omci, data, p)
}
+func decodeDownloadSectionResponseExtended(data []byte, p gopacket.PacketBuilder) error {
+ omci := &DownloadSectionResponse{}
+ omci.MsgLayerType = LayerTypeDownloadSectionResponse
+ omci.Extended = true
+ return decodingLayerDecoder(omci, data, p)
+}
+
// SerializeTo provides serialization of an Download Section Response message
func (omci *DownloadSectionResponse) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
// Basic (common) OMCI Header is 8 octets, 10
@@ -2495,17 +2567,27 @@
if omci.EntityClass != me.SoftwareImageClassID {
return me.NewProcessingError("invalid Entity Class for Download Section response")
}
- bytes, err := b.AppendBytes(2)
- if err != nil {
- return err
- }
if omci.Result > me.DeviceBusy {
msg := fmt.Sprintf("invalid results for Download Section response: %v, must be 0..6",
omci.Result)
return errors.New(msg)
}
- bytes[0] = byte(omci.Result)
- bytes[1] = omci.SectionNumber
+ if omci.Extended {
+ bytes, err := b.AppendBytes(4)
+ if err != nil {
+ return err
+ }
+ binary.BigEndian.PutUint16(bytes, uint16(2))
+ bytes[2] = byte(omci.Result)
+ bytes[3] = omci.SectionNumber
+ } else {
+ bytes, err := b.AppendBytes(2)
+ if err != nil {
+ return err
+ }
+ bytes[0] = byte(omci.Result)
+ bytes[1] = omci.SectionNumber
+ }
return nil
}
diff --git a/messagetypes_test.go b/messagetypes_test.go
index 9a9d2e7..23e020c 100644
--- a/messagetypes_test.go
+++ b/messagetypes_test.go
@@ -1654,12 +1654,12 @@
assert.True(t, ok2)
assert.NotNil(t, request)
assert.Equal(t, uint8(0xcc), request.SectionNumber)
- assert.Equal(t, 31, len(request.SectionData))
+ assert.Equal(t, MaxDownloadSectionLength, len(request.SectionData))
sectionData, genErr := stringToPacket("01020304050607080910111213141516171819202122232425262728293031")
assert.Nil(t, genErr)
assert.NotNil(t, sectionData)
- assert.Equal(t, 31, len(sectionData))
+ assert.Equal(t, MaxDownloadSectionLength, len(sectionData))
assert.Equal(t, sectionData, request.SectionData[:])
// Verify string output for message
@@ -1698,7 +1698,7 @@
sectionData, genErr := stringToPacket("01020304050607080910111213141516171819202122232425262728293031")
assert.Nil(t, genErr)
assert.NotNil(t, sectionData)
- assert.Equal(t, 31, len(sectionData))
+ assert.Equal(t, MaxDownloadSectionLength, len(sectionData))
assert.Equal(t, sectionData, request.SectionData[:])
// Verify string output for message
@@ -1718,7 +1718,7 @@
sectionData, genErr := stringToPacket("01020304050607080910111213141516171819202122232425262728293031")
assert.Nil(t, genErr)
assert.NotNil(t, sectionData)
- assert.Equal(t, 31, len(sectionData))
+ assert.Equal(t, MaxDownloadSectionLength, len(sectionData))
request := &DownloadSectionRequest{
MeBasePacket: MeBasePacket{
@@ -1726,9 +1726,44 @@
// Default Instance ID is 0
},
SectionNumber: 0xcc,
+ SectionData: sectionData,
}
- copy(request.SectionData[:], sectionData)
+ // Test serialization back to former string
+ var options gopacket.SerializeOptions
+ options.FixLengths = true
+ buffer := gopacket.NewSerializeBuffer()
+ err := gopacket.SerializeLayers(buffer, options, omciLayer, request)
+ assert.NoError(t, err)
+
+ outgoingPacket := buffer.Bytes()
+ reconstituted := packetToString(outgoingPacket)
+ assert.Equal(t, strings.ToLower(goodMessage), reconstituted)
+}
+
+func TestDownloadSectionRequestSerializeNoResponsePartialDataExpected(t *testing.T) {
+ // If a small buffer is provided, serialize will now zero extend the baseline format
+ goodMessage := "0123140a00070000cc0102030405060708091011121314151617181920212223242526272829000000000028"
+
+ omciLayer := &OMCI{
+ TransactionID: 0x0123,
+ MessageType: DownloadSectionRequestType,
+ // DeviceIdentifier: omci.BaselineIdent, // Optional, defaults to Baseline
+ // Length: 0x28, // Optional, defaults to 40 octets
+ }
+ sectionData, genErr := stringToPacket("0102030405060708091011121314151617181920212223242526272829")
+ assert.Nil(t, genErr)
+ assert.NotNil(t, sectionData)
+ assert.Equal(t, MaxDownloadSectionLength-2, len(sectionData)) // Partial data buffer
+
+ request := &DownloadSectionRequest{
+ MeBasePacket: MeBasePacket{
+ EntityClass: me.SoftwareImageClassID,
+ // Default Instance ID is 0
+ },
+ SectionNumber: 0xcc,
+ SectionData: sectionData,
+ }
// Test serialization back to former string
var options gopacket.SerializeOptions
options.FixLengths = true
@@ -1755,7 +1790,7 @@
sectionData, genErr := stringToPacket("01020304050607080910111213141516171819202122232425262728293031")
assert.Nil(t, genErr)
assert.NotNil(t, sectionData)
- assert.Equal(t, 31, len(sectionData))
+ assert.Equal(t, MaxDownloadSectionLength, len(sectionData))
request := &DownloadSectionRequest{
MeBasePacket: MeBasePacket{
@@ -1763,9 +1798,8 @@
// Default Instance ID is 0
},
SectionNumber: 0xcc,
+ SectionData: sectionData,
}
- copy(request.SectionData[:], sectionData)
-
// Test serialization back to former string
var options gopacket.SerializeOptions
options.FixLengths = true
@@ -1792,7 +1826,7 @@
sectionData, genErr := stringToPacket("01020304050607080910111213141516171819202122232425262728293031")
assert.Nil(t, genErr)
assert.NotNil(t, sectionData)
- assert.Equal(t, 31, len(sectionData))
+ assert.Equal(t, MaxDownloadSectionLength, len(sectionData))
request := &DownloadSectionRequest{
MeBasePacket: MeBasePacket{
@@ -1800,9 +1834,8 @@
EntityInstance: 0x0001, // Default is zero, here we want image 1
},
SectionNumber: 0xcc,
+ SectionData: sectionData,
}
- copy(request.SectionData[:], sectionData)
-
// Test serialization back to former string
var options gopacket.SerializeOptions
options.FixLengths = true
@@ -3064,6 +3097,32 @@
assert.NotZero(t, len(packetString))
}
+func TestExtendedGetRequestDecodeTruncated(t *testing.T) {
+ goodMessage := "035e490b010100000002ff"
+ data, err := stringToPacket(goodMessage)
+ assert.NoError(t, err)
+
+ packet := gopacket.NewPacket(data, LayerTypeOMCI, gopacket.NoCopy)
+ assert.NotNil(t, packet)
+
+ failure := packet.ErrorLayer()
+ assert.NotNil(t, failure)
+
+ decodeFailure, ok := failure.(*gopacket.DecodeFailure)
+ assert.NotNil(t, decodeFailure)
+ assert.True(t, ok)
+ assert.NotNil(t, decodeFailure.String())
+ assert.True(t, len(decodeFailure.String()) > 0)
+
+ metadata := packet.Metadata()
+ assert.NotNil(t, metadata)
+ assert.True(t, metadata.Truncated)
+
+ // Verify string output for message
+ packetString := packet.String()
+ assert.NotZero(t, len(packetString))
+}
+
func TestExtendedGetRequestSerialize(t *testing.T) {
goodMessage := "035e490b010100000002fffc"
@@ -3254,6 +3313,348 @@
assert.Equal(t, strings.ToLower(goodMessage), reconstituted)
}
+// 1 2 3 4
+// 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4
+// 0008140a00070001 - Download section, AR=0
+// cc - Section 0xcc
+// 01020304050607080910111213141516171819202122232425262728293031
+// 00000028
+func TestExtendedDownloadSectionRequestDecodeNoResponseExpected(t *testing.T) {
+ goodMessage := "0008140b00070001"
+ payloadFragment := "01020304050607080910111213141516171819202122232425"
+ payloadTotal := payloadFragment + payloadFragment + payloadFragment + payloadFragment +
+ payloadFragment + payloadFragment + payloadFragment + payloadFragment
+ sectionNumber := 0x88
+ length := 1 + (8 * 25)
+ hdr := fmt.Sprintf("%04x%02x", length, sectionNumber)
+ goodMessage += hdr + payloadTotal
+ data, err := stringToPacket(goodMessage)
+ assert.NoError(t, err)
+
+ packet := gopacket.NewPacket(data, LayerTypeOMCI, gopacket.NoCopy)
+ assert.NotNil(t, packet)
+ assert.Nil(t, packet.ErrorLayer())
+
+ omciLayer := packet.Layer(LayerTypeOMCI)
+ assert.NotNil(t, omciLayer)
+
+ omciMsg, ok := omciLayer.(*OMCI)
+ assert.True(t, ok)
+ assert.Equal(t, uint16(0x0008), omciMsg.TransactionID)
+ assert.Equal(t, DownloadSectionRequestType, omciMsg.MessageType)
+ assert.False(t, omciMsg.ResponseExpected)
+ assert.Equal(t, ExtendedIdent, omciMsg.DeviceIdentifier)
+ assert.Equal(t, uint16(length), omciMsg.Length)
+
+ msgLayer := packet.Layer(LayerTypeDownloadSectionRequest)
+ assert.NotNil(t, msgLayer)
+
+ request, ok2 := msgLayer.(*DownloadSectionRequest)
+ assert.True(t, ok2)
+ assert.NotNil(t, request)
+ assert.Equal(t, uint8(sectionNumber), request.SectionNumber)
+ assert.Equal(t, length-1, len(request.SectionData))
+
+ data, err = stringToPacket(payloadTotal)
+ assert.NoError(t, err)
+ assert.Equal(t, data, request.SectionData[:])
+
+ // Verify string output for message
+ packetString := packet.String()
+ assert.NotZero(t, len(packetString))
+}
+
+func TestExtendedDownloadSectionRequestDecodeResponseExpected(t *testing.T) {
+ goodMessage := "0008540b00070001"
+ payloadFragment := "01020304050607080910111213141516171819202122232425"
+ payloadTotal := payloadFragment + payloadFragment + payloadFragment + payloadFragment +
+ payloadFragment + payloadFragment + payloadFragment + payloadFragment +
+ payloadFragment + payloadFragment + payloadFragment + payloadFragment +
+ payloadFragment + payloadFragment + payloadFragment + payloadFragment +
+ payloadFragment + payloadFragment + payloadFragment + payloadFragment
+ sectionNumber := 0x88
+ length := 1 + (20 * 25)
+ hdr := fmt.Sprintf("%04x%02x", length, sectionNumber)
+ goodMessage += hdr + payloadTotal
+ data, err := stringToPacket(goodMessage)
+ assert.NoError(t, err)
+
+ packet := gopacket.NewPacket(data, LayerTypeOMCI, gopacket.NoCopy)
+ assert.NotNil(t, packet)
+ assert.Nil(t, packet.ErrorLayer())
+
+ omciLayer := packet.Layer(LayerTypeOMCI)
+ assert.NotNil(t, omciLayer)
+
+ omciMsg, ok := omciLayer.(*OMCI)
+ assert.True(t, ok)
+ assert.Equal(t, uint16(0x0008), omciMsg.TransactionID)
+ assert.Equal(t, DownloadSectionRequestWithResponseType, omciMsg.MessageType)
+ assert.True(t, omciMsg.ResponseExpected)
+ assert.Equal(t, ExtendedIdent, omciMsg.DeviceIdentifier)
+ assert.Equal(t, uint16(length), omciMsg.Length)
+
+ msgLayer := packet.Layer(LayerTypeDownloadSectionRequest)
+ assert.NotNil(t, msgLayer)
+
+ request, ok2 := msgLayer.(*DownloadSectionRequest)
+ assert.True(t, ok2)
+ assert.NotNil(t, request)
+ assert.Equal(t, uint8(sectionNumber), request.SectionNumber)
+ assert.Equal(t, length-1, len(request.SectionData))
+
+ data, err = stringToPacket(payloadTotal)
+ assert.NoError(t, err)
+ assert.Equal(t, data, request.SectionData)
+
+ // Verify string output for message
+ packetString := packet.String()
+ assert.NotZero(t, len(packetString))
+}
+
+func TestExtendedDownloadSectionRequestDecodeTruncated(t *testing.T) {
+ goodMessage := "0008540b000700010000"
+ data, err := stringToPacket(goodMessage)
+ assert.NoError(t, err)
+
+ packet := gopacket.NewPacket(data, LayerTypeOMCI, gopacket.NoCopy)
+ assert.NotNil(t, packet)
+
+ failure := packet.ErrorLayer()
+ assert.NotNil(t, failure)
+
+ decodeFailure, ok := failure.(*gopacket.DecodeFailure)
+ assert.NotNil(t, decodeFailure)
+ assert.True(t, ok)
+ assert.NotNil(t, decodeFailure.String())
+ assert.True(t, len(decodeFailure.String()) > 0)
+
+ metadata := packet.Metadata()
+ assert.NotNil(t, metadata)
+ assert.True(t, metadata.Truncated)
+
+ // Verify string output for message
+ packetString := packet.String()
+ assert.NotZero(t, len(packetString))
+}
+
+func TestExtendedDownloadSectionRequestSerializeNoResponseExpected(t *testing.T) {
+ goodMessage := "0123140b00070001"
+ payloadFragment := "01020304050607080910111213141516171819202122232425"
+ payloadTotal := payloadFragment + payloadFragment + payloadFragment + payloadFragment +
+ payloadFragment + payloadFragment + payloadFragment + payloadFragment
+ sectionNumber := 0x84
+ length := 1 + (8 * 25)
+ hdr := fmt.Sprintf("%04x%02x", length, sectionNumber)
+ goodMessage += hdr + payloadTotal
+
+ omciLayer := &OMCI{
+ TransactionID: 0x0123,
+ MessageType: DownloadSectionRequestType,
+ DeviceIdentifier: ExtendedIdent,
+ }
+ sectionData, genErr := stringToPacket(payloadTotal)
+ assert.Nil(t, genErr)
+ assert.NotNil(t, sectionData)
+ assert.Equal(t, len(payloadTotal)/2, len(sectionData))
+
+ request := &DownloadSectionRequest{
+ MeBasePacket: MeBasePacket{
+ EntityClass: me.SoftwareImageClassID,
+ EntityInstance: uint16(1),
+ Extended: true,
+ },
+ SectionNumber: byte(sectionNumber),
+ SectionData: sectionData,
+ }
+ // Test serialization back to former string
+ var options gopacket.SerializeOptions
+ options.FixLengths = true
+
+ buffer := gopacket.NewSerializeBuffer()
+ err := gopacket.SerializeLayers(buffer, options, omciLayer, request)
+ assert.NoError(t, err)
+
+ outgoingPacket := buffer.Bytes()
+ reconstituted := packetToString(outgoingPacket)
+ assert.Equal(t, strings.ToLower(goodMessage), reconstituted)
+}
+
+func TestExtendedDownloadSectionRequestSerializeResponseExpectedMethod1(t *testing.T) {
+ goodMessage := "2468540b00070001"
+ payloadFragment := "01020304050607080910111213141516171819202122232425"
+ payloadTotal := payloadFragment + payloadFragment + payloadFragment + payloadFragment +
+ payloadFragment + payloadFragment + payloadFragment + payloadFragment
+ sectionNumber := 0x84
+ length := 1 + (8 * 25)
+ hdr := fmt.Sprintf("%04x%02x", length, sectionNumber)
+ goodMessage += hdr + payloadTotal
+
+ omciLayer := &OMCI{
+ TransactionID: 0x2468,
+ MessageType: DownloadSectionRequestType, // or DownloadSectionRequestWithResponseType
+ ResponseExpected: true,
+ DeviceIdentifier: ExtendedIdent,
+ }
+ sectionData, genErr := stringToPacket(payloadTotal)
+ assert.Nil(t, genErr)
+ assert.NotNil(t, sectionData)
+ assert.Equal(t, len(payloadTotal)/2, len(sectionData))
+
+ request := &DownloadSectionRequest{
+ MeBasePacket: MeBasePacket{
+ EntityClass: me.SoftwareImageClassID,
+ EntityInstance: uint16(1),
+ Extended: true,
+ },
+ SectionNumber: byte(sectionNumber),
+ SectionData: sectionData,
+ }
+ // Test serialization back to former string
+ var options gopacket.SerializeOptions
+ options.FixLengths = true
+
+ buffer := gopacket.NewSerializeBuffer()
+ err := gopacket.SerializeLayers(buffer, options, omciLayer, request)
+ assert.NoError(t, err)
+
+ outgoingPacket := buffer.Bytes()
+ reconstituted := packetToString(outgoingPacket)
+ assert.Equal(t, strings.ToLower(goodMessage), reconstituted)
+}
+
+func TestExtendedDownloadSectionRequestSerializeResponseExpectedMethod2(t *testing.T) {
+ goodMessage := "2468540b00070001"
+ payloadFragment := "01020304050607080910111213141516171819202122232425"
+ payloadTotal := payloadFragment + payloadFragment + payloadFragment + payloadFragment +
+ payloadFragment + payloadFragment + payloadFragment + payloadFragment
+ sectionNumber := 0x84
+ length := 1 + (8 * 25)
+ hdr := fmt.Sprintf("%04x%02x", length, sectionNumber)
+ goodMessage += hdr + payloadTotal
+
+ // In this case, just use the request type with AR response requested already encoded
+ omciLayer := &OMCI{
+ TransactionID: 0x2468,
+ MessageType: DownloadSectionRequestWithResponseType,
+ ResponseExpected: true,
+ DeviceIdentifier: ExtendedIdent,
+ }
+ sectionData, genErr := stringToPacket(payloadTotal)
+ assert.Nil(t, genErr)
+ assert.NotNil(t, sectionData)
+ assert.Equal(t, len(payloadTotal)/2, len(sectionData))
+
+ request := &DownloadSectionRequest{
+ MeBasePacket: MeBasePacket{
+ EntityClass: me.SoftwareImageClassID,
+ EntityInstance: 0x0001, // Default is zero, here we want image 1
+ Extended: true,
+ },
+ SectionNumber: byte(sectionNumber),
+ SectionData: sectionData,
+ }
+ // Test serialization back to former string
+ var options gopacket.SerializeOptions
+ options.FixLengths = true
+
+ buffer := gopacket.NewSerializeBuffer()
+ err := gopacket.SerializeLayers(buffer, options, omciLayer, request)
+ assert.NoError(t, err)
+
+ outgoingPacket := buffer.Bytes()
+ reconstituted := packetToString(outgoingPacket)
+ assert.Equal(t, strings.ToLower(goodMessage), reconstituted)
+}
+
+func TestExtendedDownloadSectionResponseDecode(t *testing.T) {
+ goodMessage := "0022340b000700010002061f"
+ data, err := stringToPacket(goodMessage)
+ assert.NoError(t, err)
+
+ 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.Equal(t, omciMsg.TransactionID, uint16(0x0022))
+ assert.Equal(t, omciMsg.MessageType, DownloadSectionResponseType)
+ assert.Equal(t, omciMsg.DeviceIdentifier, ExtendedIdent)
+ assert.Equal(t, uint16(2), omciMsg.Length)
+
+ msgLayer := packet.Layer(LayerTypeDownloadSectionResponse)
+ assert.NotNil(t, msgLayer)
+
+ response, ok2 := msgLayer.(*DownloadSectionResponse)
+ assert.True(t, ok2)
+ assert.NotNil(t, response)
+ assert.Equal(t, me.DeviceBusy, response.Result)
+ assert.Equal(t, byte(0x1f), response.SectionNumber)
+
+ // Verify string output for message
+ packetString := packet.String()
+ assert.NotZero(t, len(packetString))
+}
+
+func TestExtendedDownloadSectionResponseDecodeTruncated(t *testing.T) {
+ goodMessage := "0022340b00070001000106"
+ data, err := stringToPacket(goodMessage)
+ assert.NoError(t, err)
+
+ packet := gopacket.NewPacket(data, LayerTypeOMCI, gopacket.NoCopy)
+ assert.NotNil(t, packet)
+
+ failure := packet.ErrorLayer()
+ assert.NotNil(t, failure)
+
+ decodeFailure, ok := failure.(*gopacket.DecodeFailure)
+ assert.NotNil(t, decodeFailure)
+ assert.True(t, ok)
+ assert.NotNil(t, decodeFailure.String())
+ assert.True(t, len(decodeFailure.String()) > 0)
+
+ metadata := packet.Metadata()
+ assert.NotNil(t, metadata)
+ assert.True(t, metadata.Truncated)
+
+ // Verify string output for message
+ packetString := packet.String()
+ assert.NotZero(t, len(packetString))
+}
+
+func TestExtendedDownloadSectionResponseSerialize(t *testing.T) {
+ goodMessage := "0022340b000700010002061f"
+
+ omciLayer := &OMCI{
+ TransactionID: 0x0022,
+ MessageType: DownloadSectionResponseType,
+ DeviceIdentifier: ExtendedIdent,
+ }
+ request := &DownloadSectionResponse{
+ MeBasePacket: MeBasePacket{
+ EntityClass: me.SoftwareImageClassID,
+ EntityInstance: 1,
+ Extended: true,
+ },
+ Result: me.DeviceBusy,
+ SectionNumber: 0x1f,
+ }
+ // Test serialization back to former string
+ var options gopacket.SerializeOptions
+ options.FixLengths = true
+
+ buffer := gopacket.NewSerializeBuffer()
+ err := gopacket.SerializeLayers(buffer, options, omciLayer, request)
+ assert.NoError(t, err)
+
+ outgoingPacket := buffer.Bytes()
+ reconstituted := packetToString(outgoingPacket)
+ assert.Equal(t, strings.ToLower(goodMessage), reconstituted)
+}
+
func TestGenericTestResultDecode(t *testing.T) {
// ONU-G ME for this test with just made up data
payload := "1234567890123456789012345678901234567890123456789012345678901234"
diff --git a/omci.go b/omci.go
index b2995d0..2f6d2d9 100644
--- a/omci.go
+++ b/omci.go
@@ -90,6 +90,10 @@
// attribute portion of the message contents and does not include the Result Code & Attribute Mask.
const MaxAttributeGetNextBaselineLength = MaxBaselineLength - 11 - 8
+// MaxDownloadSectionLength is the maximum payload size for section data of
+// a Download Section request message for the baseline message set.
+const MaxDownloadSectionLength = 31
+
// MaxTestRequestLength is the maximum payload size for test request message
// for the baseline message set.
const MaxTestRequestLength = MaxBaselineLength - 8 - 8
@@ -109,6 +113,10 @@
// message contents and does not include the Result Code & Attribute Mask.
const MaxAttributeGetNextExtendedLength = MaxExtendedLength - 13 - 4
+// MaxDownloadSectionExtendedLength is the maximum payload size for section data of
+// a Download Section request message for the extended message set.
+const MaxDownloadSectionExtendedLength = MaxExtendedLength - 11 - 4
+
// NullEntityID is often used as the Null/void Managed Entity ID for attributes
// that are used to refer to other Managed Entities but are currently not provisioned.
const NullEntityID = uint16(0xffff)