blob: 57ca21516d0608a1e75a3529542c75a24ec9d5e7 [file] [log] [blame]
/*
* Copyright 2018-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
import (
"bytes"
"encoding/binary"
"encoding/hex"
"fmt"
"strings"
"github.com/google/gopacket"
"github.com/opencord/bbsim/internal/common"
"github.com/opencord/omci-lib-go/v2"
me "github.com/opencord/omci-lib-go/v2/generated"
)
// MibDbEntry contains all the information needed to build a
// MibUploadNextResponse packet.
// if Packet has a value all the other fields are ignored and the packet is sent as is.
type MibDbEntry struct {
classId me.ClassID
entityId EntityID
params me.AttributeValueMap
packet []byte
}
type MibDb struct {
NumberOfBaselineCommands uint16
NumberOfExtendedCommands uint16
baselineItems []MibDbEntry
extendedResponses [][]byte
}
type EntityID []byte
const (
unknownMePktReportedMeHdr string = "002500018000"
unknownMePktAttributes string = "0102030405060708090A0B0C0D0E0F101112131415161718191A"
unknownAttribPktReportedMeHdr string = "0101000007FD"
unknownAttribPktAttributes string = "00400801000800000006007F07003F00010001000100"
extRespMsgContentsLenStart = 8
extRespMsgContentsLenEnd = 10
)
func (e EntityID) ToString() string {
return hex.EncodeToString(e)
}
func (e EntityID) ToUint16() uint16 {
return binary.BigEndian.Uint16(e)
}
func (e EntityID) ToUint32() uint32 {
return binary.BigEndian.Uint32(e)
}
func (e EntityID) FromUint16(id uint16) EntityID {
buff := new(bytes.Buffer)
err := binary.Write(buff, binary.BigEndian, id)
if err != nil {
panic(err)
}
return buff.Bytes()
}
func (e EntityID) Equals(i EntityID) bool {
if res := bytes.Compare(e, i); res == 0 {
return true
}
return false
}
const (
cardHolderOnuType byte = 0x01 // ONU is a single piece of integrated equipment
ethernetUnitType byte = 0x2f // Ethernet BASE-T
xgsPonUnitType byte = 0xee // XG-PON10G10
gPonUnitType byte = 0xf5 // GPON12441244
potsUnitType byte = 0x20 // POTS
cardHolderSlotID byte = 0x01
tcontSlotId byte = 0x80 // why is this not the same as the cardHolderSlotID, it does not point to anything
aniGId byte = 0x01
upstreamPriorityQueues = 8 // Number of queues for each T-CONT
downstreamPriorityQueues = 16 // Number of queues for each PPTP
tconts = 8 // NOTE will we ever need to configure this?
// trafficSchedulers = 8 // NOTE will we ever need to configure this?
)
var (
cardHolderEntityID = EntityID{cardHolderOnuType, cardHolderSlotID}
circuitPackEntityID = cardHolderEntityID // is the same as that of the cardholder ME containing this circuit pack instance
)
func GenerateUniPortEntityId(id uint32) EntityID {
return EntityID{cardHolderSlotID, byte(id)}
}
// creates a MIB database for a ONU
// CircuitPack and CardHolder are static, everything else can be configured
func GenerateMibDatabase(ethUniPortCount int, potsUniPortCount int, technology common.PonTechnology) (*MibDb, error) {
mibDb := MibDb{
baselineItems: []MibDbEntry{},
}
// the first element to return is the ONU-Data
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.OnuDataClassID,
EntityID{0x00, 0x00},
me.AttributeValueMap{me.OnuData_MibDataSync: 0}, // FIXME this needs to be parametrized before sending the response
nil,
})
// then we report the CardHolder
// NOTE we have not report it till now, so leave it commented out
//mibDb.items = append(mibDb.items, MibDbEntry{
// me.CardholderClassID,
// cardHolderEntityID,
// me.AttributeValueMap{
// "ActualPlugInUnitType": cardHolderOnuType,
// "ExpectedPlugInUnitType": ethernetUnitType,
// },
//})
// ANI circuitPack
var aniCPType byte
switch technology {
case common.XGSPON:
aniCPType = xgsPonUnitType
case common.GPON:
aniCPType = gPonUnitType
}
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.CircuitPackClassID,
circuitPackEntityID,
me.AttributeValueMap{
me.CircuitPack_Type: aniCPType,
me.CircuitPack_NumberOfPorts: 1, // NOTE is this the ANI port? must be
me.CircuitPack_SerialNumber: ToOctets("BBSM-Circuit-Pack-ani", 20),
me.CircuitPack_Version: ToOctets("v0.0.1", 20),
},
nil,
})
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.CircuitPackClassID,
circuitPackEntityID,
me.AttributeValueMap{
me.CircuitPack_VendorId: ToOctets("ONF", 4),
me.CircuitPack_AdministrativeState: 0,
me.CircuitPack_OperationalState: 0,
me.CircuitPack_BridgedOrIpInd: 0,
},
nil,
})
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.CircuitPackClassID,
circuitPackEntityID,
me.AttributeValueMap{
me.CircuitPack_EquipmentId: ToOctets("BBSM-Circuit-Pack", 20),
me.CircuitPack_CardConfiguration: 0,
me.CircuitPack_TotalTContBufferNumber: 8,
me.CircuitPack_TotalPriorityQueueNumber: 8,
me.CircuitPack_TotalTrafficSchedulerNumber: 0,
},
nil,
})
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.CircuitPackClassID,
circuitPackEntityID,
me.AttributeValueMap{
me.CircuitPack_PowerShedOverride: uint32(0),
},
nil,
})
// ANI-G
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.AniGClassID,
EntityID{tcontSlotId, aniGId},
me.AttributeValueMap{
me.AniG_Arc: 0,
me.AniG_ArcInterval: 0,
me.AniG_Deprecated: 0,
me.AniG_GemBlockLength: 48,
me.AniG_LowerOpticalThreshold: 255,
me.AniG_LowerTransmitPowerThreshold: 129,
me.AniG_OnuResponseTime: 0,
me.AniG_OpticalSignalLevel: 57428,
me.AniG_PiggybackDbaReporting: 0,
me.AniG_SignalDegradeThreshold: 9,
me.AniG_SignalFailThreshold: 5,
me.AniG_SrIndication: 1,
me.AniG_TotalTcontNumber: 8,
me.AniG_TransmitOpticalLevel: 3171,
me.AniG_UpperOpticalThreshold: 255,
me.AniG_UpperTransmitPowerThreshold: 129,
},
nil,
})
// circuitPack Ethernet
// NOTE the circuit pack is divided in multiple messages as too big to fit in a single one
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.CircuitPackClassID,
circuitPackEntityID,
me.AttributeValueMap{
me.CircuitPack_Type: ethernetUnitType,
me.CircuitPack_NumberOfPorts: ethUniPortCount,
me.CircuitPack_SerialNumber: ToOctets("BBSM-Circuit-Pack", 20),
me.CircuitPack_Version: ToOctets("v0.0.1", 20),
},
nil,
})
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.CircuitPackClassID,
circuitPackEntityID,
me.AttributeValueMap{
me.CircuitPack_VendorId: ToOctets("ONF", 4),
me.CircuitPack_AdministrativeState: 0,
me.CircuitPack_OperationalState: 0,
me.CircuitPack_BridgedOrIpInd: 0,
},
nil,
})
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.CircuitPackClassID,
circuitPackEntityID,
me.AttributeValueMap{
me.CircuitPack_EquipmentId: ToOctets("BBSM-Circuit-Pack", 20),
me.CircuitPack_CardConfiguration: 0,
me.CircuitPack_TotalTContBufferNumber: 8,
me.CircuitPack_TotalPriorityQueueNumber: 8,
me.CircuitPack_TotalTrafficSchedulerNumber: 16,
},
nil,
})
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.CircuitPackClassID,
circuitPackEntityID,
me.AttributeValueMap{
me.CircuitPack_PowerShedOverride: uint32(0),
},
nil,
})
if potsUniPortCount > 0 {
// circuitPack POTS
// NOTE the circuit pack is divided in multiple messages as too big to fit in a single one
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.CircuitPackClassID,
circuitPackEntityID,
me.AttributeValueMap{
me.CircuitPack_Type: potsUnitType,
me.CircuitPack_NumberOfPorts: potsUniPortCount,
me.CircuitPack_SerialNumber: ToOctets("BBSM-Circuit-Pack", 20),
me.CircuitPack_Version: ToOctets("v0.0.1", 20),
},
nil,
})
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.CircuitPackClassID,
circuitPackEntityID,
me.AttributeValueMap{
me.CircuitPack_VendorId: ToOctets("ONF", 4),
me.CircuitPack_AdministrativeState: 0,
me.CircuitPack_OperationalState: 0,
me.CircuitPack_BridgedOrIpInd: 0,
},
nil,
})
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.CircuitPackClassID,
circuitPackEntityID,
me.AttributeValueMap{
me.CircuitPack_EquipmentId: ToOctets("BBSM-Circuit-Pack", 20),
me.CircuitPack_CardConfiguration: 0,
me.CircuitPack_TotalTContBufferNumber: 8,
me.CircuitPack_TotalPriorityQueueNumber: 8,
me.CircuitPack_TotalTrafficSchedulerNumber: 16,
},
nil,
})
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.CircuitPackClassID,
circuitPackEntityID,
me.AttributeValueMap{
me.CircuitPack_PowerShedOverride: uint32(0),
},
nil,
})
}
// PPTP and UNI-Gs
// NOTE this are dependent on the number of UNI this ONU supports
// Through an identical ID, the UNI-G ME is implicitly linked to an instance of a PPTP
totalPortsCount := ethUniPortCount + potsUniPortCount
for i := 1; i <= totalPortsCount; i++ {
uniEntityId := GenerateUniPortEntityId(uint32(i))
if i <= ethUniPortCount {
// first, create the correct amount of ethernet UNIs, the same is done in onu.go
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.PhysicalPathTerminationPointEthernetUniClassID,
uniEntityId,
me.AttributeValueMap{
me.PhysicalPathTerminationPointEthernetUni_ExpectedType: 0,
me.PhysicalPathTerminationPointEthernetUni_SensedType: ethernetUnitType,
me.PhysicalPathTerminationPointEthernetUni_AutoDetectionConfiguration: 0,
me.PhysicalPathTerminationPointEthernetUni_EthernetLoopbackConfiguration: 0,
me.PhysicalPathTerminationPointEthernetUni_AdministrativeState: 0,
me.PhysicalPathTerminationPointEthernetUni_OperationalState: 0,
me.PhysicalPathTerminationPointEthernetUni_ConfigurationInd: 3,
me.PhysicalPathTerminationPointEthernetUni_MaxFrameSize: 1518,
me.PhysicalPathTerminationPointEthernetUni_DteOrDceInd: 0,
me.PhysicalPathTerminationPointEthernetUni_PauseTime: 0,
me.PhysicalPathTerminationPointEthernetUni_BridgedOrIpInd: 2,
me.PhysicalPathTerminationPointEthernetUni_Arc: 0,
me.PhysicalPathTerminationPointEthernetUni_ArcInterval: 0,
me.PhysicalPathTerminationPointEthernetUni_PppoeFilter: 0,
me.PhysicalPathTerminationPointEthernetUni_PowerControl: 0,
},
nil,
})
} else {
// the remaining ones are pots UNIs, the same is done in onu.go
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.PhysicalPathTerminationPointPotsUniClassID,
uniEntityId,
me.AttributeValueMap{
me.PhysicalPathTerminationPointPotsUni_AdministrativeState: 0,
me.PhysicalPathTerminationPointPotsUni_Deprecated: 0,
me.PhysicalPathTerminationPointPotsUni_Arc: 0,
me.PhysicalPathTerminationPointPotsUni_ArcInterval: 0,
me.PhysicalPathTerminationPointPotsUni_Impedance: 0,
me.PhysicalPathTerminationPointPotsUni_TransmissionPath: 0,
me.PhysicalPathTerminationPointPotsUni_RxGain: 0,
me.PhysicalPathTerminationPointPotsUni_TxGain: 0,
me.PhysicalPathTerminationPointPotsUni_OperationalState: 0,
me.PhysicalPathTerminationPointPotsUni_HookState: 0,
me.PhysicalPathTerminationPointPotsUni_PotsHoldoverTime: 0,
me.PhysicalPathTerminationPointPotsUni_NominalFeedVoltage: 0,
me.PhysicalPathTerminationPointPotsUni_LossOfSoftswitch: 0,
},
nil,
})
}
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.UniGClassID,
uniEntityId,
me.AttributeValueMap{
me.UniG_AdministrativeState: 0,
me.UniG_Deprecated: 0,
me.UniG_ManagementCapability: 0,
me.UniG_NonOmciManagementIdentifier: 0,
me.UniG_RelayAgentOptions: 0,
},
nil,
})
// Downstream Queues (related to PPTP)
// 16 priorities queues for each UNI Ports
// EntityID = cardHolderSlotID + Uni EntityID (0101)
for j := 1; j <= downstreamPriorityQueues; j++ {
queueEntityId := EntityID{cardHolderSlotID, byte(j)}
// we first report the PriorityQueue without any attribute
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.PriorityQueueClassID,
queueEntityId, //was not reported in the original implementation
me.AttributeValueMap{},
nil,
})
// then we report it with the required attributes
// In the downstream direction, the first byte is the slot number and the second byte is the port number of the queue's destination port.
relatedPort := append(uniEntityId, 0x00, byte(j))
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.PriorityQueueClassID,
queueEntityId, //was not reported in the original implementation
me.AttributeValueMap{
me.PriorityQueue_QueueConfigurationOption: 0,
me.PriorityQueue_MaximumQueueSize: 100,
me.PriorityQueue_AllocatedQueueSize: 100,
me.PriorityQueue_DiscardBlockCounterResetInterval: 0,
me.PriorityQueue_ThresholdValueForDiscardedBlocksDueToBufferOverflow: 0,
me.PriorityQueue_RelatedPort: relatedPort.ToUint32(),
me.PriorityQueue_TrafficSchedulerPointer: 0, //it was hardcoded to 0x0108 in the current implementation
me.PriorityQueue_Weight: 1,
me.PriorityQueue_BackPressureOperation: 1,
me.PriorityQueue_BackPressureTime: 0,
me.PriorityQueue_BackPressureOccurQueueThreshold: 0,
me.PriorityQueue_BackPressureClearQueueThreshold: 0,
},
nil,
})
}
}
// T-CONTS and Traffic Schedulers
for i := 1; i <= tconts; i++ {
tcontEntityId := EntityID{tcontSlotId, byte(i)}
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.TContClassID,
tcontEntityId,
me.AttributeValueMap{
me.TCont_AllocId: 65535,
},
nil,
})
tsEntityId := EntityID{cardHolderSlotID, byte(i)}
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.TrafficSchedulerClassID,
tsEntityId, //was not reported in the original implementation
me.AttributeValueMap{
me.TrafficScheduler_TContPointer: tcontEntityId.ToUint16(), // was hardcoded to a non-existing t-cont
me.TrafficScheduler_TrafficSchedulerPointer: 0,
me.TrafficScheduler_Policy: 02,
me.TrafficScheduler_PriorityWeight: 0,
},
nil,
})
for j := 1; j <= upstreamPriorityQueues; j++ {
queueEntityId := EntityID{tcontSlotId, byte(j)}
// Upstream Queues (related to traffic schedulers)
// 8 priorities queues per TCONT
// EntityID = tcontSlotId + Uni EntityID (8001)
// we first report the PriorityQueue without any attribute
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.PriorityQueueClassID,
queueEntityId, //was not reported in the original implementation
me.AttributeValueMap{},
nil,
})
// then we report it with the required attributes
// In the upstream direction, the first 2 bytes are the ME ID of the associated T- CONT, the first byte of which is a slot number, the second byte a T-CONT number.
relatedPort := append(tcontEntityId, 0x00, byte(j))
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.PriorityQueueClassID,
queueEntityId, //was not reported in the original implementation
me.AttributeValueMap{
me.PriorityQueue_QueueConfigurationOption: 0,
me.PriorityQueue_MaximumQueueSize: 100,
me.PriorityQueue_AllocatedQueueSize: 100,
me.PriorityQueue_DiscardBlockCounterResetInterval: 0,
me.PriorityQueue_ThresholdValueForDiscardedBlocksDueToBufferOverflow: 0,
me.PriorityQueue_RelatedPort: relatedPort.ToUint32(),
me.PriorityQueue_TrafficSchedulerPointer: tsEntityId.ToUint16(), //it was hardcoded to 0x0108 in the current implementation
me.PriorityQueue_Weight: 1,
me.PriorityQueue_BackPressureOperation: 1,
me.PriorityQueue_BackPressureTime: 0,
me.PriorityQueue_BackPressureOccurQueueThreshold: 0,
me.PriorityQueue_BackPressureClearQueueThreshold: 0,
},
nil,
})
}
}
// ONU-2g
onu2g := MibDbEntry{
me.Onu2GClassID,
EntityID{0x00, 0x00},
me.AttributeValueMap{
//me.Onu2G_EquipmentId: 1,
//me.Onu2G_OpticalNetworkUnitManagementAndControlChannelOmccVersion: 2,
//me.Onu2G_VendorProductCode: 3,
//me.Onu2G_SecurityCapability: 4,
//me.Onu2G_SecurityMode: 5,
me.Onu2G_TotalPriorityQueueNumber: 64,
me.Onu2G_TotalTrafficSchedulerNumber: 8,
me.Onu2G_Deprecated: 1,
me.Onu2G_TotalGemPortIdNumber: 8,
me.Onu2G_Sysuptime: 6,
me.Onu2G_ConnectivityCapability: 127,
me.Onu2G_CurrentConnectivityMode: 7,
me.Onu2G_QualityOfServiceQosConfigurationFlexibility: 63,
me.Onu2G_PriorityQueueScaleFactor: 1,
},
nil,
}
if common.Config.BBSim.InjectOmciUnknownAttributes {
onu2g = MibDbEntry{
me.Onu2GClassID,
nil,
me.AttributeValueMap{},
[]byte{129, 41, 46, 10, 0, 2, 0, 0, 1, 1, 0, 0, 7, 253, 0, 64, 8, 1, 0, 8, 0, 0, 0, 6, 0, 127, 7, 0, 63, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 40},
}
}
mibDb.baselineItems = append(mibDb.baselineItems, onu2g)
if common.Config.BBSim.InjectOmciUnknownMe {
// NOTE the TxID is actually replaced
// by SetTxIdInEncodedPacket in CreateMibUploadNextResponse
txId := uint16(33066)
b := make([]byte, 4)
binary.BigEndian.PutUint16(b, txId)
b[2] = byte(omci.MibUploadNextResponseType)
b[3] = byte(omci.BaselineIdent)
omciHdr := hex.EncodeToString(b)
//omciHdr := "00032e0a"
msgHdr := "00020000"
trailer := "0000002828ce00e2"
msg := omciHdr + msgHdr + unknownMePktReportedMeHdr + unknownMePktAttributes + trailer
data, err := hex.DecodeString(msg)
if err != nil {
omciLogger.Fatal("cannot-create-custom-packet")
}
packet := gopacket.NewPacket(data, omci.LayerTypeOMCI, gopacket.Lazy)
mibDb.baselineItems = append(mibDb.baselineItems, MibDbEntry{
me.ClassID(37), // G.988 "Intentionally left blank"
nil,
me.AttributeValueMap{},
packet.Data(),
})
}
mibDb.NumberOfBaselineCommands = uint16(len(mibDb.baselineItems))
// Create extended MIB upload responses
omciLayer := &omci.OMCI{
TransactionID: 0xFFFF, //to be replaced later on
MessageType: omci.MibUploadNextResponseType,
DeviceIdentifier: omci.ExtendedIdent,
}
var i uint16 = 0
for i < mibDb.NumberOfBaselineCommands {
currentEntry := mibDb.baselineItems[i]
for mibDb.baselineItems[i].packet != nil {
// Skip any entry with a predefined packet currently used for MEs with unknown ClassID or unknown attributes.
// This information will be added later to the last extended response packet
i++
currentEntry = mibDb.baselineItems[i]
}
reportedME, meErr := me.LoadManagedEntityDefinition(currentEntry.classId, me.ParamData{
EntityID: currentEntry.entityId.ToUint16(),
Attributes: currentEntry.params,
})
if meErr.GetError() != nil {
omciLogger.Errorf("Error while generating reportedME %s: %v", currentEntry.classId.String(), meErr.Error())
}
request := &omci.MibUploadNextResponse{
MeBasePacket: omci.MeBasePacket{
EntityClass: me.OnuDataClassID,
EntityInstance: uint16(0),
Extended: true,
},
ReportedME: *reportedME,
AdditionalMEs: make([]me.ManagedEntity, 0),
}
i++
var outgoingPacket []byte
var outgoingPacketLen = 0
addMeLoop:
for outgoingPacketLen <= omci.MaxExtendedLength-omci.MaxBaselineLength && i < mibDb.NumberOfBaselineCommands {
currentEntry := mibDb.baselineItems[i]
for mibDb.baselineItems[i].packet != nil {
// Skip any entry with a predefined packet currently used for MEs with unknown ClassID or unknown attributes.
// This information will be added later to the last extended response packet
i++
if i < mibDb.NumberOfBaselineCommands {
currentEntry = mibDb.baselineItems[i]
} else {
break addMeLoop
}
}
additionalME, meErr := me.LoadManagedEntityDefinition(currentEntry.classId, me.ParamData{
EntityID: currentEntry.entityId.ToUint16(),
Attributes: currentEntry.params,
})
if meErr.GetError() != nil {
omciLogger.Errorf("Error while generating additionalME %s: %v", currentEntry.classId.String(), meErr.Error())
}
request.AdditionalMEs = append(request.AdditionalMEs, *additionalME)
var options gopacket.SerializeOptions
options.FixLengths = true
buffer := gopacket.NewSerializeBuffer()
omciErr := gopacket.SerializeLayers(buffer, options, omciLayer, request)
if omciErr != nil {
omciLogger.Errorf("Error while serializing generating additionalME %s: %v", currentEntry.classId.String(), omciErr)
}
outgoingPacket = buffer.Bytes()
outgoingPacketLen = len(outgoingPacket)
i++
}
mibDb.extendedResponses = append(mibDb.extendedResponses, outgoingPacket)
mibDb.NumberOfExtendedCommands = uint16(len(mibDb.extendedResponses))
outgoingPacketString := strings.ToLower(hex.EncodeToString(mibDb.extendedResponses[mibDb.NumberOfExtendedCommands-1]))
omciLogger.Debugf("Extended MIB upload response respNo: %d length: %d string: %s", mibDb.NumberOfExtendedCommands, outgoingPacketLen, outgoingPacketString)
}
// Currently, there is enough space in the last extended response to add potential MEs with unknown ClassID or unknown attributes, if requested.
if common.Config.BBSim.InjectOmciUnknownMe {
var err error
mibDb.extendedResponses[mibDb.NumberOfExtendedCommands-1], err = AppendAdditionalMEs(mibDb.extendedResponses[mibDb.NumberOfExtendedCommands-1], unknownMePktReportedMeHdr, unknownMePktAttributes)
if err != nil {
omciLogger.Fatal(err)
} else {
outgoingPacketString := strings.ToLower(hex.EncodeToString(mibDb.extendedResponses[mibDb.NumberOfExtendedCommands-1]))
omciLogger.Debugf("Reponse with unknown ME added: %s", outgoingPacketString)
}
}
if common.Config.BBSim.InjectOmciUnknownAttributes {
var err error
mibDb.extendedResponses[mibDb.NumberOfExtendedCommands-1], err = AppendAdditionalMEs(mibDb.extendedResponses[mibDb.NumberOfExtendedCommands-1], unknownAttribPktReportedMeHdr, unknownAttribPktAttributes)
if err != nil {
omciLogger.Fatal(err)
} else {
outgoingPacketString := strings.ToLower(hex.EncodeToString(mibDb.extendedResponses[mibDb.NumberOfExtendedCommands-1]))
omciLogger.Debugf("Reponse with unknown attributes added: %s", outgoingPacketString)
}
}
return &mibDb, nil
}
func AppendAdditionalMEs(srcSlice []byte, reportedMeHdr string, attributes string) ([]byte, error) {
attribBytes, err := hex.DecodeString(attributes)
if err != nil {
return nil, fmt.Errorf("cannot-decode-attributes-string")
}
attribBytesLen := len(attribBytes)
attribBytesLenStr := fmt.Sprintf("%04X", attribBytesLen)
msg := attribBytesLenStr + reportedMeHdr + attributes
data, err := hex.DecodeString(msg)
if err != nil {
return nil, fmt.Errorf("cannot-decode-attributes")
}
dstSlice := make([]byte, len(srcSlice))
copy(dstSlice, srcSlice)
dstSlice = append(dstSlice[:], data[:]...)
messageContentsLen := binary.BigEndian.Uint16(dstSlice[extRespMsgContentsLenStart:extRespMsgContentsLenEnd])
dataLen := len(data)
newMessageContentsLen := messageContentsLen + uint16(dataLen)
newLenSlice := make([]byte, 2)
binary.BigEndian.PutUint16(newLenSlice, newMessageContentsLen)
copy(dstSlice[extRespMsgContentsLenStart:extRespMsgContentsLenEnd], newLenSlice[0:2])
return dstSlice, nil
}