[VOL-1349] EPON ONU adapter (package B)
Change-Id: I609ba349c429bc7e87c74b66bb1121841f9caef6
diff --git a/vendor/github.com/opencord/omci-lib-go/omci.go b/vendor/github.com/opencord/omci-lib-go/omci.go
new file mode 100644
index 0000000..7a162d6
--- /dev/null
+++ b/vendor/github.com/opencord/omci-lib-go/omci.go
@@ -0,0 +1,331 @@
+/*
+ * 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.
+ */
+
+// Package omci provides a library of routines to create, manipulate, serialize, and
+// decode ITU-T G.988 OMCI messages/packets
+package omci
+
+import (
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "github.com/aead/cmac/aes"
+ me "github.com/opencord/omci-lib-go/generated"
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+// DeviceIdent identifies the OMCI message format. Currently either baseline or extended.
+type DeviceIdent byte
+
+// LayerTypeOmci provide a gopacket LayerType for OMCI messages
+var (
+ LayerTypeOMCI gopacket.LayerType
+)
+
+func init() {
+ LayerTypeOMCI = gopacket.RegisterLayerType(1000,
+ gopacket.LayerTypeMetadata{
+ Name: "OMCI",
+ Decoder: gopacket.DecodeFunc(decodeOMCI),
+ })
+}
+
+const (
+ // Device Identifiers
+ _ = iota
+ // BaselineIdent message are composed of a fixed 40 octet packet + 8-octet trailer. All
+ // G-PON OLTs and ONUs support the baseline message set
+ BaselineIdent DeviceIdent = 0x0A
+
+ // ExtendedIdent messager are up to 1920 octets but may not be supported by all ONUs or OLTs.
+ ExtendedIdent DeviceIdent = 0x0B
+)
+
+var omciIK = []byte{0x18, 0x4b, 0x8a, 0xd4, 0xd1, 0xac, 0x4a, 0xf4,
+ 0xdd, 0x4b, 0x33, 0x9e, 0xcc, 0x0d, 0x33, 0x70}
+
+func (di DeviceIdent) String() string {
+ switch di {
+ default:
+ return "Unknown"
+
+ case BaselineIdent:
+ return "Baseline"
+
+ case ExtendedIdent:
+ return "Extended"
+ }
+}
+
+// MaxBaselineLength is the maximum number of octets allowed in an OMCI Baseline
+// message. Depending on the adapter, it may or may not include the
+const MaxBaselineLength = 48
+
+// MaxExtendedLength is the maximum number of octets allowed in an OMCI Extended
+// message (including header).
+const MaxExtendedLength = 1980
+
+// MaxAttributeMibUploadNextBaselineLength is the maximum payload size for attributes for
+// a Baseline MIB Upload Next message.29
+const MaxAttributeMibUploadNextBaselineLength = MaxBaselineLength - 14 - 8
+
+// MaxAttributeGetNextBaselineLength is the maximum payload size for attributes for
+// a Baseline MIB Get Next message. This is just the attribute portion of the
+// message contents and does not include the Result Code & Attribute Mask.
+const MaxAttributeGetNextBaselineLength = MaxBaselineLength - 11 - 8
+
+// MaxManagedEntityMibUploadNextExtendedLength is the maximum payload size for ME
+// entries for an Extended MIB Upload Next message. Extended messages differ from
+// the baseline as multiple MEs can be reported in a single frame, just not multiple
+// attributes.
+const MaxManagedEntityMibUploadNextExtendedLength = MaxExtendedLength - 10 - 4
+
+// MaxAttributeGetNextExtendedLength is the maximum payload size for attributes for
+// a Extended MIB Get Next message. This is just the attribute portion of the
+// message contents and does not include the Result Code & Attribute Mask.
+const MaxAttributeGetNextExtendedLength = MaxExtendedLength - 13 - 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)
+
+// OMCI defines the common protocol. Extended will be added once
+// I can get basic working (and layered properly). See ITU-T G.988 11/2017 section
+// A.3 for more information
+type OMCI struct {
+ layers.BaseLayer
+ TransactionID uint16
+ MessageType MessageType
+ DeviceIdentifier DeviceIdent
+ Payload []byte
+ padding []byte
+ Length uint16
+ MIC uint32
+}
+
+func (omci *OMCI) String() string {
+ //msgType := me.MsgType(byte(omci.MessageType) & me.MsgTypeMask)
+ //if me.IsAutonomousNotification(msgType) {
+ // return fmt.Sprintf("OMCI: Type: %v:", msgType)
+ //} else if byte(omci.MessageType)&me.AK == me.AK {
+ // return fmt.Sprintf("OMCI: Type: %v Response", msgType)
+ //}
+ return fmt.Sprintf("Type: %v, TID: %d (%#x), Ident: %v",
+ omci.MessageType, omci.TransactionID, omci.TransactionID, omci.DeviceIdentifier)
+}
+
+// LayerType returns LayerTypeOMCI
+func (omci *OMCI) LayerType() gopacket.LayerType {
+ return LayerTypeOMCI
+}
+
+// LayerContents returns the OMCI specific layer information
+func (omci *OMCI) LayerContents() []byte {
+ b := make([]byte, 8)
+ binary.BigEndian.PutUint16(b, omci.TransactionID)
+ b[2] = byte(omci.MessageType)
+ b[3] = byte(omci.DeviceIdentifier)
+ return b
+}
+
+// CanDecode returns the layers that this class can decode
+func (omci *OMCI) CanDecode() gopacket.LayerClass {
+ return LayerTypeOMCI
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (omci *OMCI) NextLayerType() gopacket.LayerType {
+ return gopacket.LayerTypeZero
+}
+
+func decodeOMCI(data []byte, p gopacket.PacketBuilder) error {
+ // Allow baseline messages without Length & MIC, but no less
+ if len(data) < MaxBaselineLength-8 {
+ return errors.New("frame header too small")
+ }
+ switch DeviceIdent(data[3]) {
+ default:
+ return errors.New("unsupported message type")
+
+ case BaselineIdent:
+ //omci := &BaselineMessage{}
+ omci := &OMCI{}
+ return omci.DecodeFromBytes(data, p)
+
+ case ExtendedIdent:
+ //omci := &ExtendedMessage{}
+ omci := &OMCI{}
+ return omci.DecodeFromBytes(data, p)
+ }
+}
+
+func calculateMicAes128(data []byte) (uint32, error) {
+ // See if upstream or downstream
+ var downstreamCDir = [...]byte{0x01}
+ var upstreamCDir = [...]byte{0x02}
+
+ tid := binary.BigEndian.Uint16(data[0:2])
+ var sum []byte
+ var err error
+
+ if (data[2]&me.AK) == me.AK || tid == 0 {
+ sum, err = aes.Sum(append(upstreamCDir[:], data[:44]...), omciIK, 4)
+ } else {
+ sum, err = aes.Sum(append(downstreamCDir[:], data[:44]...), omciIK, 4)
+ }
+ if err != nil {
+ return 0, err
+ }
+ return binary.BigEndian.Uint32(sum), nil
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Baseline Message encode / decode
+
+// DecodeFromBytes will decode the OMCI layer of a packet/message
+func (omci *OMCI) DecodeFromBytes(data []byte, p gopacket.PacketBuilder) error {
+ if len(data) < 10 {
+ p.SetTruncated()
+ return errors.New("frame too small")
+ }
+ omci.TransactionID = binary.BigEndian.Uint16(data[0:])
+ omci.MessageType = MessageType(data[2])
+ omci.DeviceIdentifier = DeviceIdent(data[3])
+
+ isNotification := (int(omci.MessageType) & ^me.MsgTypeMask) == 0
+ if omci.TransactionID == 0 && !isNotification {
+ return errors.New("omci Transaction ID is zero for non-Notification type message")
+ }
+ // Decode length
+ var payloadOffset int
+ var micOffset int
+ if omci.DeviceIdentifier == BaselineIdent {
+ omci.Length = MaxBaselineLength - 8
+ payloadOffset = 8
+ micOffset = MaxBaselineLength - 4
+
+ if len(data) >= micOffset {
+ length := binary.BigEndian.Uint32(data[micOffset-4:])
+ if uint16(length) != omci.Length {
+ return me.NewProcessingError("invalid baseline message length")
+ }
+ }
+ } else {
+ payloadOffset = 10
+ omci.Length = binary.BigEndian.Uint16(data[8:10])
+ micOffset = int(omci.Length) + payloadOffset
+
+ if omci.Length > MaxExtendedLength {
+ return me.NewProcessingError("extended frame exceeds maximum allowed")
+ }
+ if int(omci.Length) != micOffset {
+ if int(omci.Length) < micOffset {
+ p.SetTruncated()
+ }
+ return me.NewProcessingError("extended frame too small")
+ }
+ }
+ // Extract MIC if present in the data
+ if len(data) >= micOffset+4 {
+ omci.MIC = binary.BigEndian.Uint32(data[micOffset:])
+ actual, _ := calculateMicAes128(data[:micOffset])
+ if omci.MIC != actual {
+ _ = fmt.Sprintf("invalid MIC, expected %#x, got %#x",
+ omci.MIC, actual)
+ //return errors.New(msg)
+ }
+ }
+ omci.BaseLayer = layers.BaseLayer{data[:4], data[4:]}
+ p.AddLayer(omci)
+ nextLayer, err := MsgTypeToNextLayer(omci.MessageType)
+ if err != nil {
+ return err
+ }
+ return p.NextDecoder(nextLayer)
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (omci *OMCI) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+ // TODO: Hardcoded for baseline message format for now. Will eventually need to support
+ // the extended message format.
+ bytes, err := b.PrependBytes(4)
+ if err != nil {
+ return err
+ }
+ // OMCI layer error checks
+ isNotification := (int(omci.MessageType) & ^me.MsgTypeMask) == 0
+ if omci.TransactionID == 0 && !isNotification {
+ return errors.New("omci Transaction ID is zero for non-Notification type message")
+ }
+ if omci.DeviceIdentifier == 0 {
+ omci.DeviceIdentifier = BaselineIdent // Allow uninitialized device identifier
+ }
+ if omci.DeviceIdentifier == BaselineIdent {
+ if omci.Length == 0 {
+ omci.Length = MaxBaselineLength - 8 // Allow uninitialized length
+ } else if omci.Length != MaxBaselineLength-8 {
+ msg := fmt.Sprintf("invalid Baseline message length: %v", omci.Length)
+ return errors.New(msg)
+ }
+ } else if omci.DeviceIdentifier == ExtendedIdent {
+ if omci.Length == 0 {
+ omci.Length = uint16(len(bytes) - 10) // Allow uninitialized length
+ }
+ if omci.Length > MaxExtendedLength {
+ msg := fmt.Sprintf("invalid Baseline message length: %v", omci.Length)
+ return errors.New(msg)
+ }
+ } else {
+ msg := fmt.Sprintf("invalid device identifier: %#x, Baseline or Extended expected",
+ omci.DeviceIdentifier)
+ return errors.New(msg)
+ }
+ binary.BigEndian.PutUint16(bytes, omci.TransactionID)
+ bytes[2] = byte(omci.MessageType)
+ bytes[3] = byte(omci.DeviceIdentifier)
+ b.PushLayer(LayerTypeOMCI)
+
+ bufLen := len(b.Bytes())
+ padSize := int(omci.Length) - bufLen + 4
+ if padSize < 0 {
+ msg := fmt.Sprintf("invalid OMCI Message Type length, exceeded allowed frame size by %d bytes",
+ -padSize)
+ return errors.New(msg)
+ }
+ padding, err := b.AppendBytes(padSize)
+ copy(padding, lotsOfZeros[:])
+
+ if omci.DeviceIdentifier == BaselineIdent {
+ // For baseline, always provide the length
+ binary.BigEndian.PutUint32(b.Bytes()[MaxBaselineLength-8:], 40)
+ }
+ if opts.ComputeChecksums {
+ micBytes, err := b.AppendBytes(4)
+ if err != nil {
+ return err
+ }
+ omci.MIC, _ = calculateMicAes128(bytes[:MaxBaselineLength-4])
+ binary.BigEndian.PutUint32(micBytes, omci.MIC)
+ }
+ return nil
+}
+
+// hacky way to zero out memory... there must be a better way?
+var lotsOfZeros [MaxExtendedLength]byte // Extended OMCI messages may be up to 1980 bytes long, including headers