blob: 58bd6cff481f921fc8d03a502b9e6a5585524950 [file] [log] [blame]
/*
* 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 (
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"reflect"
"sort"
"strings"
mapset "github.com/deckarep/golang-set"
"github.com/google/gopacket"
)
// Attribute types
type AttributeType uint8
const (
UnknownAttributeType AttributeType = iota // Not known
OctetsAttributeType // Series of zero or more octets
StringAttributeType // Readable String
UnsignedIntegerAttributeType // Integer (0..max)
TableAttributeType // Table (of Octets)
SignedIntegerAttributeType // Signed integer, often expressed as 2's complement
PointerAttributeType // Managed Entity ID or pointer to a Managed instance
BitFieldAttributeType // Bitfield
EnumerationAttributeType // Fixed number of values (Unsigned Integers)
CounterAttributeType // Incrementing counter
)
// AttributeDefinitionMap is a map of attribute definitions with the attribute index (0..15)
// as the key
type AttributeDefinitionMap map[uint]AttributeDefinition
// AttributeDefinition defines a single specific Managed Entity's attributes
type AttributeDefinition struct {
Name string
AttributeType AttributeType
Index uint
Mask uint16
DefValue interface{}
Size int // Size of attribute in bytes. 0 indicates variable/unknown
Access mapset.Set // AttributeAccess...
Constraint func(interface{}) *ParamError
Avc bool // If true, an AVC notification can occur for the attribute
Tca bool // If true, a threshold crossing alert alarm notification can occur for the attribute
Optional bool // If true, attribute is option, else mandatory
Deprecated bool // If true, attribute is deprecated
}
func (attr *AttributeDefinition) String() string {
return fmt.Sprintf("AttributeDefinition: %v (%v/%v): Size: %v, Default: %v, Access: %v",
attr.GetName(), attr.AttributeType, attr.GetIndex(), attr.GetSize(), attr.GetDefault(), attr.GetAccess())
}
// GetName returns the attribute's name
func (attr AttributeDefinition) GetName() string { return attr.Name }
// GetIndex returns the attribute index )0..15)
func (attr AttributeDefinition) GetIndex() uint { return attr.Index }
// GetDefault provides the default value for an attribute if not specified
// during its creation
func (attr AttributeDefinition) GetDefault() interface{} { return attr.DefValue }
// GetSize returns the size of the attribute. For table attributes, the size is
// the size of a single table.
func (attr AttributeDefinition) GetSize() int { return attr.Size }
// GetAccess provides the access information (Read, Write, ...)
func (attr AttributeDefinition) GetAccess() mapset.Set { return attr.Access }
// GetConstraints returns a function that can be called for the attribute
// that will validate the value. An appropriate error is returned if the
// constraint fails, otherwise nil is returned to indicate that the value
// is valid.
func (attr AttributeDefinition) GetConstraints() func(interface{}) *ParamError {
return attr.Constraint
}
// IsTableAttribute returns true if the attribute is a table
func (attr AttributeDefinition) IsTableAttribute() bool {
return attr.AttributeType == TableAttributeType
}
// IsCounter returns true if the attribute is a counter (usually expressed as an
// unsigned integer)
func (attr AttributeDefinition) IsCounter() bool {
return attr.AttributeType == CounterAttributeType
}
// IsBitField returns true if the attribute is a bitfield
func (attr AttributeDefinition) IsBitField() bool {
return attr.AttributeType == BitFieldAttributeType
}
// IsString returns true if the attribute is a string. Strings are typically encoded
// into fixed length files and padded with 0's
func (attr AttributeDefinition) IsString() bool {
return attr.AttributeType == StringAttributeType
}
// Decode takes a slice of bytes and converts them into a value appropriate for
// the attribute, or returns an error on failure
func (attr *AttributeDefinition) Decode(data []byte, df gopacket.DecodeFeedback, msgType byte) (interface{}, error) {
if attr.IsTableAttribute() {
value, err := attr.tableAttributeDecode(data, df, msgType)
if err != nil {
return nil, err
}
if attr.GetConstraints() != nil {
if omciErr := attr.GetConstraints()(value); omciErr != nil {
return nil, omciErr.GetError()
}
}
return value, nil
}
size := attr.GetSize()
if len(data) < size {
df.SetTruncated()
return nil, NewMessageTruncatedError("packet too small for field")
}
switch attr.AttributeType {
case StringAttributeType, OctetsAttributeType, UnknownAttributeType:
value := make([]byte, size)
copy(value, data[:size])
if attr.GetConstraints() != nil {
if omciErr := attr.GetConstraints()(value); omciErr != nil {
return nil, omciErr.GetError()
}
}
return value, nil
default:
switch attr.GetSize() {
default:
value := make([]byte, size)
copy(value, data[:size])
if attr.GetConstraints() != nil {
if omciErr := attr.GetConstraints()(value); omciErr != nil {
return nil, omciErr.GetError()
}
}
return value, nil
case 1:
value := data[0]
if attr.GetConstraints() != nil {
if omciErr := attr.GetConstraints()(value); omciErr != nil {
return nil, omciErr.GetError()
}
}
return value, nil
case 2:
value := binary.BigEndian.Uint16(data[0:2])
if attr.GetConstraints() != nil {
if omciErr := attr.GetConstraints()(value); omciErr != nil {
return nil, omciErr.GetError()
}
}
return value, nil
case 4:
value := binary.BigEndian.Uint32(data[0:4])
if attr.GetConstraints() != nil {
if omciErr := attr.GetConstraints()(value); omciErr != nil {
return nil, omciErr.GetError()
}
}
return value, nil
case 8:
value := binary.BigEndian.Uint64(data[0:8])
if attr.GetConstraints() != nil {
omciErr := attr.GetConstraints()(value)
if omciErr != nil {
return nil, omciErr.GetError()
}
}
return value, nil
}
}
}
// IOctetStream interface defines a way to convert a custom type to/from an octet
// stream.
type IOctetStream interface {
ToOctetString() ([]byte, error)
FromOctetString([]byte) (interface{}, error)
}
// InterfaceToOctets converts an attribute value to a string of octets
func InterfaceToOctets(input interface{}) ([]byte, error) {
switch values := input.(type) {
case []byte:
return values, nil
case []uint16:
stream := make([]byte, 2*len(values))
for index, value := range values {
binary.BigEndian.PutUint16(stream[index*2:], value)
}
return stream, nil
case []uint32:
stream := make([]byte, 4*len(values))
for index, value := range values {
binary.BigEndian.PutUint32(stream[index*4:], value)
}
return stream, nil
case []uint64:
stream := make([]byte, 8*len(values))
for index, value := range values {
binary.BigEndian.PutUint64(stream[index*8:], value)
}
return stream, nil
case IOctetStream:
return values.ToOctetString()
default:
var typeName string
if t := reflect.TypeOf(input); t.Kind() == reflect.Ptr {
typeName = "*" + t.Elem().Name()
} else {
typeName = t.Name()
}
return nil, fmt.Errorf("unable to convert input to octet string: %v", typeName)
}
}
// SerializeTo takes an attribute value and converts it to a slice of bytes ready
// for transmission
func (attr *AttributeDefinition) SerializeTo(value interface{}, b gopacket.SerializeBuffer,
msgType byte, bytesAvailable int) (int, error) {
if attr.IsTableAttribute() {
return attr.tableAttributeSerializeTo(value, b, msgType, bytesAvailable)
}
if value == nil {
return 0, fmt.Errorf("attribute: %v is nil", attr.Name)
}
size := attr.GetSize()
if bytesAvailable < size {
return 0, NewMessageTruncatedError(fmt.Sprintf("not enough space for attribute: %v", attr.Name))
}
bytes, err := b.AppendBytes(size)
if err != nil {
return 0, err
}
switch attr.AttributeType {
case StringAttributeType, OctetsAttributeType, UnknownAttributeType:
byteStream, err := InterfaceToOctets(value)
if err != nil {
return 0, err
}
copy(bytes, byteStream)
default:
switch size {
default:
byteStream, err := InterfaceToOctets(value)
if err != nil {
return 0, err
}
copy(bytes, byteStream)
case 1:
switch value.(type) {
case int:
bytes[0] = byte(value.(int))
default:
bytes[0] = value.(byte)
}
case 2:
switch value.(type) {
case int:
binary.BigEndian.PutUint16(bytes, uint16(value.(int)))
default:
binary.BigEndian.PutUint16(bytes, value.(uint16))
}
case 4:
switch value.(type) {
case int:
binary.BigEndian.PutUint32(bytes, uint32(value.(int)))
default:
binary.BigEndian.PutUint32(bytes, value.(uint32))
}
case 8:
switch value.(type) {
case int:
binary.BigEndian.PutUint64(bytes, uint64(value.(int)))
default:
binary.BigEndian.PutUint64(bytes, value.(uint64))
}
}
}
return size, nil
}
// BufferToTableAttributes takes the reconstructed octet buffer transmitted for
// a table attribute (over many GetNextResponses) and converts it into the desired
// format for each table row
func (attr *AttributeDefinition) BufferToTableAttributes(data []byte) (interface{}, error) {
// Source is network byte order octets. Convert to proper array of slices
rowSize := attr.GetSize()
dataSize := len(data)
index := 0
switch rowSize {
default:
value := make([][]byte, dataSize/rowSize)
for offset := 0; offset < dataSize; offset += rowSize {
value[index] = make([]byte, rowSize)
copy(value[index], data[offset:])
index++
}
return value, nil
case 1:
value := make([]byte, dataSize)
copy(value, data)
return value, nil
case 2:
value := make([]uint16, dataSize/2)
for offset := 0; offset < dataSize; offset += rowSize {
value[offset] = binary.BigEndian.Uint16(data[offset:])
index++
}
return value, nil
case 4:
value := make([]uint32, dataSize/4)
for offset := 0; offset < dataSize; offset += rowSize {
value[offset] = binary.BigEndian.Uint32(data[offset:])
index++
}
return value, nil
case 8:
value := make([]uint64, dataSize/8)
for offset := 0; offset < dataSize; offset += rowSize {
value[offset] = binary.BigEndian.Uint64(data[offset:])
index++
}
return value, nil
}
}
func (attr *AttributeDefinition) tableAttributeDecode(data []byte, df gopacket.DecodeFeedback, msgType byte) (interface{}, error) {
// Serialization of a table depends on the type of message. A
// Review of ITU-T G.988 shows that access on tables are
// either Read and/or Write, never Set-by-Create
switch msgType {
default:
return nil, fmt.Errorf("unsupported Message Type '%v' for table serialization", msgType)
case byte(Get) | AK: // Get Response
// Size
value := binary.BigEndian.Uint32(data[0:4])
return value, nil
case byte(Set) | AR: // Set Request
fallthrough
case byte(GetNext) | AK: // Get Next Response
// Block of data (octets) that need to be reassembled before conversion
// to table/row-data. If table attribute is not explicitly given a value
// we have to assume the entire data buffer is the value. The receiver of
// this frame will need to trim off any addtional information at the end
// of the last frame sequence since they (and the ONU) are the only ones
// who know how long the data really is.
size := attr.GetSize()
if size != 0 && len(data) < attr.GetSize() {
df.SetTruncated()
return nil, NewMessageTruncatedError("packet too small for field")
} else if size == 0 {
return nil, NewProcessingError("table attributes with no size are not supported: %v", attr.Name)
}
return data, nil
case byte(SetTable) | AR: // Set Table Request
// TODO: Only baseline supported at this time
return nil, errors.New("attribute encode for set-table-request not yet supported")
}
}
func (attr *AttributeDefinition) tableAttributeSerializeTo(value interface{}, b gopacket.SerializeBuffer, msgType byte,
bytesAvailable int) (int, error) {
// Serialization of a table depends on the type of message. A
// Review of ITU-T G.988 shows that access on tables are
// either Read and/or Write, never Set-by-Create
switch msgType {
default:
return 0, fmt.Errorf("unsupported Message Type '%v' for table serialization", msgType)
case byte(Get) | AK: // Get Response
// Size
if bytesAvailable < 4 {
return 0, NewMessageTruncatedError(fmt.Sprintf("not enough space for attribute: %v", attr.Name))
}
if dwordSize, ok := value.(uint32); ok {
bytes, err := b.AppendBytes(4)
if err != nil {
return 0, err
}
binary.BigEndian.PutUint32(bytes, dwordSize)
return 4, nil
}
return 0, errors.New("unexpected type for table serialization")
case byte(GetNext) | AK: // Get Next Response
// Values are already in network by order form
if data, ok := value.([]byte); ok {
if bytesAvailable < len(data) {
return 0, NewMessageTruncatedError(fmt.Sprintf("not enough space for attribute: %v", attr.Name))
}
bytes, err := b.AppendBytes(len(data))
if err != nil {
return 0, err
}
copy(bytes, data)
return len(data), nil
}
return 0, errors.New("unexpected type for table serialization")
case byte(Set) | AR: // Set Request
// TODO: For complex table types (such as extended vlan tagging config) create an
// interface definition. Provide a switch type to look for that as well as for
// value being a byte slice... For now, just byte slice provided
break
case byte(SetTable) | AR: // Set Table Request
// TODO: Only baseline supported at this time
return 0, errors.New("attribute encode for set-table-request not yet supported")
}
size := attr.GetSize()
if bytesAvailable < size {
return 0, NewMessageTruncatedError(fmt.Sprintf("not enough space for attribute: %v", attr.Name))
}
bytes, err := b.AppendBytes(size)
if err != nil {
return 0, err
}
switch attr.GetSize() {
default:
copy(bytes, value.([]byte))
case 1:
switch value.(type) {
case int:
bytes[0] = byte(value.(int))
default:
bytes[0] = value.(byte)
}
case 2:
switch value.(type) {
case int:
binary.BigEndian.PutUint16(bytes, uint16(value.(int)))
default:
binary.BigEndian.PutUint16(bytes, value.(uint16))
}
case 4:
switch value.(type) {
case int:
binary.BigEndian.PutUint32(bytes, uint32(value.(int)))
default:
binary.BigEndian.PutUint32(bytes, value.(uint32))
}
case 8:
switch value.(type) {
case int:
binary.BigEndian.PutUint64(bytes, uint64(value.(int)))
default:
binary.BigEndian.PutUint64(bytes, value.(uint64))
}
}
return size, nil
}
// GetAttributeDefinitionByName searches the attribute definition map for the
// attribute with the specified name (case insensitive)
func GetAttributeDefinitionByName(attrMap AttributeDefinitionMap, name string) (*AttributeDefinition, error) {
nameLower := strings.ToLower(name)
for _, attrVal := range attrMap {
if nameLower == strings.ToLower(attrVal.GetName()) {
return &attrVal, nil
}
}
return nil, errors.New(fmt.Sprintf("attribute '%s' not found", name))
}
// GetAttributeDefinitionMapKeys is a convenience functions since we may need to
// iterate a map in key index order. Maps in Go since v1.0 the iteration order
// of maps have been randomized.
func GetAttributeDefinitionMapKeys(attrMap AttributeDefinitionMap) []uint {
var keys []uint
for k := range attrMap {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
return keys
}
// GetAttributeBitmap returns the attribute bitmask for a single attribute
func GetAttributeBitmap(attrMap AttributeDefinitionMap, name string) (uint16, error) {
attrDef, err := GetAttributeDefinitionByName(attrMap, name)
if err != nil {
return 0, err
}
index := attrDef.GetIndex()
if index == 0 {
return 0, errors.New("managed entity ID should not be used in an attribute bitmask")
}
return uint16(1 << (16 - index)), nil
}
// GetAttributesBitmap is a convenience functions to scan a list of attributes
// and return the bitmask that represents them
func GetAttributesBitmap(attrMap AttributeDefinitionMap, attributes mapset.Set) (uint16, error) {
var mask uint16
for _, def := range attrMap {
if attributes.Contains(def.Name) {
mask |= def.Mask
attributes.Remove(def.Name)
}
}
if attributes.Cardinality() > 0 {
return 0, fmt.Errorf("unsupported attributes: %v", attributes)
}
return mask, nil
}
// GetAttributesValueMap returns the attribute value map with uninitialized values based
// on the attribute bitmask
func GetAttributesValueMap(attrDefs AttributeDefinitionMap, mask uint16, access mapset.Set) (AttributeValueMap, OmciErrors) {
attrMap := make(AttributeValueMap, 0)
for index, def := range attrDefs {
if index == 0 {
continue
}
checkMask := def.Mask
accessOk := access == nil || def.GetAccess().Intersect(access).Cardinality() > 0
if (mask&checkMask) != 0 && accessOk {
attrMap[def.GetName()] = nil
mask &= ^checkMask
}
}
if mask != 0 {
// Return map, but signaled failed attributes
return attrMap, NewParameterError(mask)
}
return attrMap, NewOmciSuccess()
}
///////////////////////////////////////////////////////////////////////
// Packet definitions for attributes of various types/sizes
func toOctets(str string) []byte {
data, err := base64.StdEncoding.DecodeString(str)
if err != nil {
panic(fmt.Sprintf("Invalid Base-64 string: '%v'", str))
}
return data
}
///////////////////////////////////////////////////////////////////////
// Packet definitions for attributes of various types/sizes
// ByteField returns an AttributeDefinition for an attribute that is encoded as a single
// octet (8-bits).
func ByteField(name string, attrType AttributeType, mask uint16, defVal uint8, access mapset.Set, avc bool,
optional bool, deprecated bool, index uint) AttributeDefinition {
return AttributeDefinition{
Name: name,
AttributeType: attrType,
Mask: mask,
Index: index,
DefValue: defVal,
Size: 1,
Access: access,
Avc: avc,
Optional: optional,
Deprecated: deprecated,
}
}
// Uint16Field returns an AttributeDefinition for an attribute that is encoded as two
// octet (16-bits).
func Uint16Field(name string, attrType AttributeType, mask uint16, defVal uint16, access mapset.Set, avc bool,
optional bool, deprecated bool, index uint) AttributeDefinition {
return AttributeDefinition{
Name: name,
AttributeType: attrType,
Mask: mask,
Index: index,
DefValue: defVal,
Size: 2,
Access: access,
Avc: avc,
Optional: optional,
Deprecated: deprecated,
}
}
// Uint32Field returns an AttributeDefinition for an attribute that is encoded as four
// octet (32-bits).
func Uint32Field(name string, attrType AttributeType, mask uint16, defVal uint32, access mapset.Set, avc bool,
optional bool, deprecated bool, index uint) AttributeDefinition {
return AttributeDefinition{
Name: name,
AttributeType: attrType,
Mask: mask,
Index: index,
DefValue: defVal,
Size: 4,
Access: access,
Avc: avc,
Optional: optional,
Deprecated: deprecated,
}
}
// Uint64Field returns an AttributeDefinition for an attribute that is encoded as eight
// octet (64-bits).
func Uint64Field(name string, attrType AttributeType, mask uint16, defVal uint64, access mapset.Set, avc bool,
optional bool, deprecated bool, index uint) AttributeDefinition {
return AttributeDefinition{
Name: name,
AttributeType: attrType,
Mask: mask,
Index: index,
DefValue: defVal,
Size: 8,
Access: access,
Avc: avc,
Optional: optional,
Deprecated: deprecated,
}
}
// MultiByteField returns an AttributeDefinition for an attribute that is encoded as multiple
// octets that do not map into fields with a length that is 1, 2, 4, or 8 octets.
func MultiByteField(name string, attrType AttributeType, mask uint16, size uint, defVal []byte, access mapset.Set, avc bool,
optional bool, deprecated bool, index uint) AttributeDefinition {
return AttributeDefinition{
Name: name,
AttributeType: attrType,
Mask: mask,
Index: index,
DefValue: defVal,
Size: int(size),
Access: access,
Avc: avc,
Optional: optional,
Deprecated: deprecated,
}
}
// Notes on various OMCI ME Table attribute fields. This comment will eventually be
// removed once a good table solution is implemented. These are not all the MEs with
// table attributes, but probably ones I care about to support initially.
//
// ME Notes
// --------------------------------------------------------------------------------------------
// Port-mapping package -> Combined Port table -> N * 25 sized rows (port (1) + ME(2) * 12)
// ONU Remote Debug -> Reply table (N bytes)
// ONU3-G -> Status snapshot recordtable M x N bytes
// MCAST Gem interworkTP-> IPv4 multicast adress table (12*n) (two 2 byte fields, two 4 byte fields)
// IPv6 multicast adress table (24*n) (various sub-fields)
// L2 mcast gem TP -> MCAST MAC addr filtering table (11 * n) (various sub-fields)
// MAC Bridge Port Filt -> MAC Filter table (8 * n) (3 fields, some are bits) *** BITS ***
// MAC Bridge Port data -> Bridge Table (8*M) (vaius fields, some are bits) *** BITS ***
// VLAN tagging filter -> Rx Vlan tag op table (16 * n) Lots of bit fields *** BITS ***
// MCAST operations profile
// MCAST Subscriber config info
// MCAST subscriber monitor
// OMCI -> Two tables (N bytes and 2*N bytes)
// General pupose buffer -> N bytes
// Enhanced security control (17 * N bytes), (16 * P Bytes) , (16 * Q bytes), and more...
//
// An early example of info to track
// TableInfo is an early prototype of how to better model some tables that are
// difficult to code.
//
// The Value member may be one of the following:
// nil : Empty, no default, ...
// value : A specific value that equates to one row, ie) 6 or toOctets("base64")
// array : One or more rows of values. [2]uint16{2, 3}
type TableInfo struct {
Value interface{} // See comment above
Size int // Table Row Size
}
func (t *TableInfo) String() string {
return fmt.Sprintf("TableInfo: Size: %d, Value(s): %v", t.Size, t.Value)
}
// TableField is used to define an attribute that is a table
func TableField(name string, attrType AttributeType, mask uint16, tableInfo TableInfo, access mapset.Set,
avc bool, optional bool, deprecated bool, index uint) AttributeDefinition {
return AttributeDefinition{
Name: name,
AttributeType: attrType,
Mask: mask,
Index: index,
DefValue: tableInfo.Value,
Size: tableInfo.Size, //Number of elements
Access: access,
Avc: avc,
Optional: optional,
Deprecated: deprecated,
}
}
// UnknownField is currently not used and may be deprecated. Its original intent
// was to be a placeholder during table attribute development
func UnknownField(name string, mask uint16, size int, index uint) AttributeDefinition {
return AttributeDefinition{
Name: name,
AttributeType: UnknownAttributeType, // Stored as octet string
Mask: mask,
Index: index,
DefValue: nil,
Size: size,
Access: mapset.NewSet(Read, Write),
Avc: false,
Optional: false,
Deprecated: false,
}
}
// AttributeValueMap maps an attribute (by name) to its value
type AttributeValueMap map[string]interface{}
// MergeInDefaultValues will examine the Manage Entity defaults (for non-SetByCreate attributes). This
// function is called on a MIB Create request but is provide for external use in case it is needed
// before the MIB entry is created
func MergeInDefaultValues(classID ClassID, attributes AttributeValueMap) OmciErrors {
// Get default values for non-SetByCreate attributes
attrDefs, err := GetAttributesDefinitions(classID)
if err.StatusCode() != Success {
return err
} else if attributes == nil {
return NewProcessingError("Invalid (nil) Attribute Value Map referenced")
}
nilAllowed := mapset.NewSet(StringAttributeType, OctetsAttributeType, TableAttributeType)
for index, attrDef := range attrDefs {
if !attrDef.Access.Contains(SetByCreate) && index != 0 &&
(attrDef.DefValue != nil || nilAllowed.Contains(attrDef.AttributeType)) {
name := attrDef.GetName()
if existing, found := attributes[name]; !found || existing == nil {
attributes[name] = attrDef.DefValue
}
}
}
return err
}