Skip to content

Commit

Permalink
Add support for SCCP Management Message encoding/decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
linouxis9 committed Apr 11, 2024
1 parent 184111b commit f10741a
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 1 deletion.
32 changes: 31 additions & 1 deletion sccp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"encoding"
"io"
"log"
"strings"
"testing"

"github.com/pascaldekloe/goe/verify"
Expand Down Expand Up @@ -95,6 +96,32 @@ var testcases = []struct {
return nil, err
}

return v, nil
},
},
{
description: "SCMG SSA",
structured: sccp.NewSCMG(sccp.SCMGTypeSSA, 9, 405, 0, 0),
serialized: []byte{0x1, 0x09, 0x95, 0x01, 0x00},
decodeFunc: func(b []byte) (serializable, error) {
v, err := sccp.ParseSCMG(b)
if err != nil {
return nil, err
}

return v, nil
},
},
{
description: "SCMG SSC",
structured: sccp.NewSCMG(sccp.SCMGTypeSSC, 9, 405, 0, 4),
serialized: []byte{0x6, 0x09, 0x95, 0x01, 0x00, 0x04},
decodeFunc: func(b []byte) (serializable, error) {
v, err := sccp.ParseSCMG(b)
if err != nil {
return nil, err
}

return v, nil
},
},
Expand Down Expand Up @@ -139,6 +166,9 @@ func TestMessages(t *testing.T) {
if _, ok := c.structured.(*sccp.Header); ok {
return
}
if _, ok := c.structured.(*sccp.SCMG); ok {
return
}

decoded, err := sccp.ParseMessage(c.serialized)
if err != nil {
Expand All @@ -158,7 +188,7 @@ func TestMessages(t *testing.T) {

func TestPartialStructuredMessages(t *testing.T) {
for _, c := range testcases {
if c.description == "Header" {
if c.description == "Header" || strings.Contains(c.description, "SCMG") {
// TODO: consider removing Header struct as it's almost useless.
continue
}
Expand Down
142 changes: 142 additions & 0 deletions scmg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// Copyright 2024 Valentin D'Emmanuele. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.

package sccp

import (
"encoding/binary"
"fmt"
"io"
)

// MsgType is type of SCMG message.
type SCMGType uint8

const (
_ SCMGType = iota
SCMGTypeSSA
SCMGTypeSSP
SCMGTypeSST
SCMGTypeSOR
SCMGTypeSOG
SCMGTypeSSC
)

// SCMG represents a SCCP Management message (SCMG).
// ITU Q.713
type SCMG struct {
Type SCMGType
AffectedSSN uint8
AffectedPC uint16
SubsystemMultiplicityIndicator uint8
SCCPCongestionLevel uint8
}

// NewSCMG creates a new SCMG.
func NewSCMG(typ SCMGType, affectedSSN uint8, affectedPC uint16, subsystemMultiplicityIndicator uint8, sccpCongestionLevel uint8) *SCMG {
s := &SCMG{
Type: typ,
AffectedSSN: affectedSSN,
AffectedPC: affectedPC,
SubsystemMultiplicityIndicator: subsystemMultiplicityIndicator,
SCCPCongestionLevel: sccpCongestionLevel,
}

return s
}

// MarshalBinary returns the byte sequence generated from a SCMG instance.
func (s *SCMG) MarshalBinary() ([]byte, error) {
b := make([]byte, s.MarshalLen())
if err := s.MarshalTo(b); err != nil {
return nil, err
}

return b, nil
}

// MarshalTo puts the byte sequence in the byte array given as b.
func (s *SCMG) MarshalTo(b []byte) error {
l := len(b)

if l < s.MarshalLen() {
return io.ErrUnexpectedEOF
}

b[0] = uint8(s.Type)
b[1] = s.AffectedSSN
binary.LittleEndian.PutUint16(b[2:4], s.AffectedPC)
b[4] = s.SubsystemMultiplicityIndicator
if s.Type == SCMGTypeSSC {
b[5] = s.SCCPCongestionLevel
}

return nil
}

// ParseSCMG decodes given byte sequence as a SCMG.
func ParseSCMG(b []byte) (*SCMG, error) {
s := &SCMG{}
if err := s.UnmarshalBinary(b); err != nil {
return nil, err
}

return s, nil
}

// UnmarshalBinary sets the values retrieved from byte sequence in a SCMG.
func (s *SCMG) UnmarshalBinary(b []byte) error {
l := len(b)
if l < 5 {
return io.ErrUnexpectedEOF
}

s.Type = SCMGType(b[0])
s.AffectedSSN = b[1]
s.AffectedPC = binary.LittleEndian.Uint16(b[2:4])
s.SubsystemMultiplicityIndicator = b[4]

if s.Type == SCMGTypeSSC {
if l < 6 {
return io.ErrUnexpectedEOF
}
s.SCCPCongestionLevel = b[5]
}

return nil
}

// MarshalLen returns the serial length.
func (s *SCMG) MarshalLen() int {
// Table 24/Q.713 – SCMG messages
l := 5

// Table 25/Q.713 – SSC
if s.Type == SCMGTypeSSC {
l += 1
}

return l
}

// String returns the SCMG values in human readable format.
func (s *SCMG) String() string {
return fmt.Sprintf("{Type: %d, Affected SSN: %v, Affected PC: %v, Subsystem Multiplicity Indicator: %d, SCCP Congestion Level: %d}",
s.Type,
s.AffectedSSN,
s.AffectedPC,
s.SubsystemMultiplicityIndicator,
s.SCCPCongestionLevel,
)
}

// MessageType returns the Message Type in int.
func (s *SCMG) MessageType() SCMGType {
return s.Type
}

// MessageTypeName returns the Message Type in string.
func (s *SCMG) MessageTypeName() string {
return "SCMG"
}

0 comments on commit f10741a

Please sign in to comment.