blob: 67a65fffbe5ba2a79df8ce78f076bc1ef36a10b3 [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 (
"encoding/hex"
"errors"
"fmt"
"github.com/google/gopacket"
"github.com/opencord/omci-lib-go"
me "github.com/opencord/omci-lib-go/generated"
"github.com/opencord/voltha-protos/v4/go/openolt"
log "github.com/sirupsen/logrus"
"math/rand"
"strconv"
)
func ParseGetRequest(omciPkt gopacket.Packet) (*omci.GetRequest, error) {
msgLayer := omciPkt.Layer(omci.LayerTypeGetRequest)
if msgLayer == nil {
err := "omci Msg layer could not be detected for LayerTypeGetRequest"
omciLogger.Error(err)
return nil, errors.New(err)
}
msgObj, msgOk := msgLayer.(*omci.GetRequest)
if !msgOk {
err := "omci Msg layer could not be assigned for LayerTypeGetRequest"
omciLogger.Error(err)
return nil, errors.New(err)
}
return msgObj, nil
}
func CreateGetResponse(omciPkt gopacket.Packet, omciMsg *omci.OMCI, onuSn *openolt.SerialNumber, mds uint8, activeImageEntityId uint16, committedImageEntityId uint16, onuDown bool) ([]byte, error) {
msgObj, err := ParseGetRequest(omciPkt)
if err != nil {
return nil, err
}
omciLogger.WithFields(log.Fields{
"EntityClass": msgObj.EntityClass,
"EntityInstance": msgObj.EntityInstance,
"AttributeMask": fmt.Sprintf("%x", msgObj.AttributeMask),
}).Trace("received-omci-get-request")
var response *omci.GetResponse
switch msgObj.EntityClass {
case me.Onu2GClassID:
response = createOnu2gResponse(msgObj.AttributeMask, msgObj.EntityInstance)
case me.OnuGClassID:
response = createOnugResponse(msgObj.AttributeMask, msgObj.EntityInstance, onuSn)
case me.SoftwareImageClassID:
response = createSoftwareImageResponse(msgObj.AttributeMask, msgObj.EntityInstance, activeImageEntityId, committedImageEntityId)
case me.IpHostConfigDataClassID:
response = createIpHostResponse(msgObj.AttributeMask, msgObj.EntityInstance)
case me.UniGClassID:
response = createUnigResponse(msgObj.AttributeMask, msgObj.EntityInstance, onuDown)
case me.PhysicalPathTerminationPointEthernetUniClassID:
response = createPptpResponse(msgObj.AttributeMask, msgObj.EntityInstance, onuDown)
case me.AniGClassID:
response = createAnigResponse(msgObj.AttributeMask, msgObj.EntityInstance)
case me.OnuDataClassID:
response = createOnuDataResponse(msgObj.AttributeMask, msgObj.EntityInstance, mds)
case me.EthernetFramePerformanceMonitoringHistoryDataUpstreamClassID:
response = createEthernetFramePerformanceMonitoringHistoryDataUpstreamResponse(msgObj.AttributeMask, msgObj.EntityInstance)
case me.EthernetFramePerformanceMonitoringHistoryDataDownstreamClassID:
response = createEthernetFramePerformanceMonitoringHistoryDataDownstreamResponse(msgObj.AttributeMask, msgObj.EntityInstance)
case me.EthernetPerformanceMonitoringHistoryDataClassID:
response = createEthernetPerformanceMonitoringHistoryDataResponse(msgObj.AttributeMask, msgObj.EntityInstance)
case me.FecPerformanceMonitoringHistoryDataClassID:
response = createFecPerformanceMonitoringHistoryDataResponse(msgObj.AttributeMask, msgObj.EntityInstance)
case me.GemPortNetworkCtpPerformanceMonitoringHistoryDataClassID:
response = createGemPortNetworkCtpPerformanceMonitoringHistoryData(msgObj.AttributeMask, msgObj.EntityInstance)
default:
omciLogger.WithFields(log.Fields{
"EntityClass": msgObj.EntityClass,
"EntityInstance": msgObj.EntityInstance,
"AttributeMask": fmt.Sprintf("%x", msgObj.AttributeMask),
}).Warnf("do-not-know-how-to-handle-get-request-for-me-class")
return nil, nil
}
pkt, err := Serialize(omci.GetResponseType, response, omciMsg.TransactionID)
if err != nil {
omciLogger.WithFields(log.Fields{
"Err": err,
"TxID": strconv.FormatInt(int64(omciMsg.TransactionID), 16),
}).Error("cannot-Serialize-Onu2gResponse")
return nil, err
}
log.WithFields(log.Fields{
"TxID": strconv.FormatInt(int64(omciMsg.TransactionID), 16),
"pkt": hex.EncodeToString(pkt),
}).Trace("omci-get-response")
return pkt, nil
}
func createOnu2gResponse(attributeMask uint16, entityID uint16) *omci.GetResponse {
managedEntity, meErr := me.NewOnu2G(me.ParamData{
EntityID: entityID,
Attributes: me.AttributeValueMap{
"ManagedEntityId": entityID,
"EquipmentId": toOctets("12345123451234512345", 20),
"OpticalNetworkUnitManagementAndControlChannelOmccVersion": 180,
"VendorProductCode": 0,
"SecurityCapability": 1,
"SecurityMode": 1,
"TotalPriorityQueueNumber": 1,
"TotalTrafficSchedulerNumber": 1,
"Deprecated": 1,
"TotalGemPortIdNumber": 32,
"Sysuptime": 319389947, // NOTE need to be smarter?
"ConnectivityCapability": 127,
"CurrentConnectivityMode": 5,
"QualityOfServiceQosConfigurationFlexibility": 48,
"PriorityQueueScaleFactor": 1,
},
})
if meErr.GetError() != nil {
omciLogger.Errorf("NewOnu2G %v", meErr.Error())
return nil
}
return &omci.GetResponse{
MeBasePacket: omci.MeBasePacket{
EntityClass: me.Onu2GClassID,
},
Attributes: managedEntity.GetAttributeValueMap(),
AttributeMask: attributeMask,
Result: me.Success,
}
}
func createOnugResponse(attributeMask uint16, entityID uint16, onuSn *openolt.SerialNumber) *omci.GetResponse {
managedEntity, meErr := me.NewOnuG(me.ParamData{
EntityID: entityID,
Attributes: me.AttributeValueMap{
"ManagedEntityId": entityID,
"VendorId": toOctets("BBSM", 4),
"Version": toOctets("v0.0.1", 14),
"SerialNumber": append(onuSn.VendorId, onuSn.VendorSpecific...),
"TrafficManagementOption": 0,
"Deprecated": 0,
"BatteryBackup": 0,
"AdministrativeState": 0,
"OperationalState": 0,
"OnuSurvivalTime": 10,
"LogicalOnuId": toOctets("BBSM", 24),
"LogicalPassword": toOctets("BBSM", 12),
"CredentialsStatus": 0,
"ExtendedTcLayerOptions": 0,
},
})
if meErr.GetError() != nil {
omciLogger.Errorf("NewOnu2G %v", meErr.Error())
return nil
}
return &omci.GetResponse{
MeBasePacket: omci.MeBasePacket{
EntityClass: me.OnuGClassID,
},
Attributes: managedEntity.GetAttributeValueMap(),
AttributeMask: attributeMask,
Result: me.Success,
}
//return &omci.GetResponse{
// MeBasePacket: omci.MeBasePacket{
// EntityClass: me.OnuGClassID,
// EntityInstance: entityID,
// },
// Attributes: me.AttributeValueMap{
//
// },
// Result: me.Success,
// AttributeMask: attributeMask,
//}
}
func createSoftwareImageResponse(attributeMask uint16, entityInstance uint16, activeImageEntityId uint16, committedImageEntityId uint16) *omci.GetResponse {
omciLogger.WithFields(log.Fields{
"EntityInstance": entityInstance,
}).Trace("received-get-software-image-request")
// Only one image can be active and committed
committed := 0
active := 0
if entityInstance == activeImageEntityId {
active = 1
}
if entityInstance == committedImageEntityId {
committed = 1
}
// NOTE that we need send the response for the correct ME Instance or the adapter won't process it
res := &omci.GetResponse{
MeBasePacket: omci.MeBasePacket{
EntityClass: me.SoftwareImageClassID,
EntityInstance: entityInstance,
},
Attributes: me.AttributeValueMap{
"ManagedEntityId": 0,
"Version": toOctets("00000000000001", 14),
"IsCommitted": committed,
"IsActive": active,
"IsValid": 1,
"ProductCode": toOctets("product-code", 25),
"ImageHash": toOctets("broadband-sim", 16),
},
Result: me.Success,
AttributeMask: attributeMask,
}
omciLogger.WithFields(log.Fields{
"omciMessage": res,
"entityId": entityInstance,
"active": active,
"committed": committed,
}).Trace("Reporting SoftwareImage")
return res
}
func createIpHostResponse(attributeMask uint16, entityInstance uint16) *omci.GetResponse {
return &omci.GetResponse{
MeBasePacket: omci.MeBasePacket{
EntityClass: me.IpHostConfigDataClassID,
EntityInstance: entityInstance,
},
Attributes: me.AttributeValueMap{
"ManagedEntityId": 0,
"MacAddress": toOctets("aabbcc", 6),
},
Result: me.Success,
AttributeMask: attributeMask,
}
}
func createUnigResponse(attributeMask uint16, entityID uint16, onuDown bool) *omci.GetResponse {
// Valid values for uni_admin_state are 0 (unlocks) and 1 (locks)
omciAdminState := 1
if !onuDown {
omciAdminState = 0
}
managedEntity, meErr := me.NewUniG(me.ParamData{
EntityID: entityID,
Attributes: me.AttributeValueMap{
"ManagedEntityId": entityID,
"Deprecated": 0,
"AdministrativeState": omciAdminState,
"ManagementCapability": 0,
"NonOmciManagementIdentifier": 1,
"RelayAgentOptions": 1,
},
})
if meErr.GetError() != nil {
omciLogger.Errorf("NewUniG %v", meErr.Error())
return nil
}
return &omci.GetResponse{
MeBasePacket: omci.MeBasePacket{
EntityClass: me.UniGClassID,
EntityInstance: entityID,
},
Attributes: managedEntity.GetAttributeValueMap(),
AttributeMask: attributeMask,
Result: me.Success,
}
}
func createPptpResponse(attributeMask uint16, entityID uint16, onuDown bool) *omci.GetResponse {
// Valid values for oper_state are 0 (enabled) and 1 (disabled)
// Valid values for uni_admin_state are 0 (unlocks) and 1 (locks)
onuAdminState := 1
if !onuDown {
onuAdminState = 0
}
onuOperState := onuAdminState // For now make the assumption that oper state reflects the admin state
managedEntity, meErr := me.NewPhysicalPathTerminationPointEthernetUni(me.ParamData{
EntityID: entityID,
Attributes: me.AttributeValueMap{
"ManagedEntityId": entityID,
"ExpectedType": 0,
"SensedType": 0,
"AutoDetectionConfiguration": 0,
"EthernetLoopbackConfiguration": 0,
"AdministrativeState": onuAdminState,
"OperationalState": onuOperState,
"ConfigurationInd": 0,
"MaxFrameSize": 0,
"DteOrDceInd": 0,
"PauseTime": 0,
"BridgedOrIpInd": 0,
"Arc": 0,
"ArcInterval": 0,
"PppoeFilter": 0,
"PowerControl": 0,
},
})
if meErr.GetError() != nil {
omciLogger.Errorf("NewPhysicalPathTerminationPointEthernetUni %v", meErr.Error())
return nil
}
return &omci.GetResponse{
MeBasePacket: omci.MeBasePacket{
EntityClass: me.PhysicalPathTerminationPointEthernetUniClassID,
EntityInstance: entityID,
},
Attributes: managedEntity.GetAttributeValueMap(),
AttributeMask: attributeMask,
Result: me.Success,
}
}
func createEthernetFramePerformanceMonitoringHistoryDataUpstreamResponse(attributeMask uint16, entityID uint16) *omci.GetResponse {
managedEntity, meErr := me.NewEthernetFramePerformanceMonitoringHistoryDataUpstream(me.ParamData{
EntityID: entityID,
Attributes: me.AttributeValueMap{
"ManagedEntityId": entityID,
"IntervalEndTime": 0, // This ideally should increment by 1 every collection interval, but staying 0 for simulation is Ok for now.
"ThresholdData12Id": 0,
"DropEvents": rand.Intn(100),
"Octets": rand.Intn(100),
"Packets": rand.Intn(100),
"BroadcastPackets": rand.Intn(100),
"MulticastPackets": rand.Intn(100),
"CrcErroredPackets": rand.Intn(100),
"UndersizePackets": rand.Intn(100),
"OversizePackets": rand.Intn(100),
"Packets64Octets": rand.Intn(100),
"Packets65To127Octets": rand.Intn(100),
"Packets128To255Octets": rand.Intn(100),
"Packets256To511Octets": rand.Intn(100),
"Packets512To1023Octets": rand.Intn(100),
"Packets1024To1518Octets": rand.Intn(100),
},
})
if meErr.GetError() != nil {
omciLogger.Errorf("NewEthernetFramePerformanceMonitoringHistoryDataUpstream %v", meErr.Error())
return nil
}
// L2 PM counters MEs exceed max allowed OMCI payload size.
// So the request/responses are always multipart.
// First identify the attributes that are not requested in the current GET request.
// Then filter out those attributes from the responses in the current GET response.
unwantedAttributeMask := ^attributeMask
var i uint16
for i = 1; i <= 16; i++ { // 1 and 16 because they are allowed valid min and max index keys in AttributeValueMap.
// We leave out 0 because that is ManagedEntity and that is a default IE in the map
if (1<<(16-i))&unwantedAttributeMask > 0 {
if err := managedEntity.DeleteAttributeByIndex(uint(i)); err != nil {
omciLogger.Errorf("error deleting attribute at index=%v, err=%v", i, err)
}
}
}
return &omci.GetResponse{
MeBasePacket: omci.MeBasePacket{
EntityClass: me.EthernetFramePerformanceMonitoringHistoryDataUpstreamClassID,
EntityInstance: entityID,
},
Attributes: managedEntity.GetAttributeValueMap(),
AttributeMask: attributeMask,
Result: me.Success,
}
}
func createEthernetFramePerformanceMonitoringHistoryDataDownstreamResponse(attributeMask uint16, entityID uint16) *omci.GetResponse {
managedEntity, meErr := me.NewEthernetFramePerformanceMonitoringHistoryDataDownstream(me.ParamData{
EntityID: entityID,
Attributes: me.AttributeValueMap{
"ManagedEntityId": entityID,
"IntervalEndTime": 0, // This ideally should increment by 1 every collection interval, but staying 0 for simulation is Ok for now.
"ThresholdData12Id": 0,
"DropEvents": rand.Intn(100),
"Octets": rand.Intn(100),
"Packets": rand.Intn(100),
"BroadcastPackets": rand.Intn(100),
"MulticastPackets": rand.Intn(100),
"CrcErroredPackets": rand.Intn(100),
"UndersizePackets": rand.Intn(100),
"OversizePackets": rand.Intn(100),
"Packets64Octets": rand.Intn(100),
"Packets65To127Octets": rand.Intn(100),
"Packets128To255Octets": rand.Intn(100),
"Packets256To511Octets": rand.Intn(100),
"Packets512To1023Octets": rand.Intn(100),
"Packets1024To1518Octets": rand.Intn(100),
},
})
if meErr.GetError() != nil {
omciLogger.Errorf("NewEthernetFramePerformanceMonitoringHistoryDataDownstream %v", meErr.Error())
return nil
}
// L2 PM counters MEs exceed max allowed OMCI payload size.
// So the request/responses are always multipart.
// First identify the attributes that are not requested in the current GET request.
// Then filter out those attributes from the responses in the current GET response.
unwantedAttributeMask := ^attributeMask
var i uint16
for i = 1; i <= 16; i++ { // 1 and 16 because they are allowed valid min and max index keys in AttributeValueMap.
// We leave out 0 because that is ManagedEntity and that is a default IE in the map
if (1<<(16-i))&unwantedAttributeMask > 0 {
if err := managedEntity.DeleteAttributeByIndex(uint(i)); err != nil {
omciLogger.Errorf("error deleting attribute at index=%v, err=%v", i, err)
}
}
}
return &omci.GetResponse{
MeBasePacket: omci.MeBasePacket{
EntityClass: me.EthernetFramePerformanceMonitoringHistoryDataDownstreamClassID,
EntityInstance: entityID,
},
Attributes: managedEntity.GetAttributeValueMap(),
AttributeMask: attributeMask,
Result: me.Success,
}
}
func createEthernetPerformanceMonitoringHistoryDataResponse(attributeMask uint16, entityID uint16) *omci.GetResponse {
managedEntity, meErr := me.NewEthernetPerformanceMonitoringHistoryData(me.ParamData{
EntityID: entityID,
Attributes: me.AttributeValueMap{
"ManagedEntityId": entityID,
"IntervalEndTime": 0, // This ideally should increment by 1 every collection interval, but staying 0 for simulation is Ok for now.
"ThresholdData12Id": 0,
"FcsErrors": rand.Intn(100),
"ExcessiveCollisionCounter": rand.Intn(100),
"LateCollisionCounter": rand.Intn(100),
"FramesTooLong": rand.Intn(100),
"BufferOverflowsOnReceive": rand.Intn(100),
"BufferOverflowsOnTransmit": rand.Intn(100),
"SingleCollisionFrameCounter": rand.Intn(100),
"MultipleCollisionsFrameCounter": rand.Intn(100),
"SqeCounter": rand.Intn(100),
"DeferredTransmissionCounter": rand.Intn(100),
"InternalMacTransmitErrorCounter": rand.Intn(100),
"CarrierSenseErrorCounter": rand.Intn(100),
"AlignmentErrorCounter": rand.Intn(100),
"InternalMacReceiveErrorCounter": rand.Intn(100),
},
})
if meErr.GetError() != nil {
omciLogger.Errorf("NewEthernetPerformanceMonitoringHistoryData %v", meErr.Error())
return nil
}
// L2 PM counters MEs exceed max allowed OMCI payload size.
// So the request/responses are always multipart.
// First identify the attributes that are not requested in the current GET request.
// Then filter out those attributes from the responses in the current GET response.
unwantedAttributeMask := ^attributeMask
var i uint16
for i = 1; i <= 16; i++ { // 1 and 16 because they are allowed valid min and max index keys in AttributeValueMap.
// We leave out 0 because that is ManagedEntity and that is a default IE in the map
if (1<<(16-i))&unwantedAttributeMask > 0 {
if err := managedEntity.DeleteAttributeByIndex(uint(i)); err != nil {
omciLogger.Errorf("error deleting attribute at index=%v, err=%v", i, err)
}
}
}
return &omci.GetResponse{
MeBasePacket: omci.MeBasePacket{
EntityClass: me.EthernetPerformanceMonitoringHistoryDataClassID,
EntityInstance: entityID,
},
Attributes: managedEntity.GetAttributeValueMap(),
AttributeMask: attributeMask,
Result: me.Success,
}
}
func createFecPerformanceMonitoringHistoryDataResponse(attributeMask uint16, entityID uint16) *omci.GetResponse {
managedEntity, meErr := me.NewFecPerformanceMonitoringHistoryData(me.ParamData{
EntityID: entityID,
Attributes: me.AttributeValueMap{
"ManagedEntityId": entityID,
"IntervalEndTime": 0, // This ideally should increment by 1 every collection interval, but staying 0 for simulation is Ok for now.
"ThresholdData12Id": 0,
"CorrectedBytes": rand.Intn(100),
"CorrectedCodeWords": rand.Intn(100),
"UncorrectableCodeWords": rand.Intn(100),
"TotalCodeWords": rand.Intn(100),
"FecSeconds": rand.Intn(100),
},
})
if meErr.GetError() != nil {
omciLogger.Errorf("NewFecPerformanceMonitoringHistoryData %v", meErr.Error())
return nil
}
// FEC History counter fits within single gem payload.
// No need of the logical we use in other Ethernet History counters or Gem Port History counters
return &omci.GetResponse{
MeBasePacket: omci.MeBasePacket{
EntityClass: me.FecPerformanceMonitoringHistoryDataClassID,
EntityInstance: entityID,
},
Attributes: managedEntity.GetAttributeValueMap(),
AttributeMask: attributeMask,
Result: me.Success,
}
}
func createGemPortNetworkCtpPerformanceMonitoringHistoryData(attributeMask uint16, entityID uint16) *omci.GetResponse {
managedEntity, meErr := me.NewGemPortNetworkCtpPerformanceMonitoringHistoryData(me.ParamData{
EntityID: entityID,
Attributes: me.AttributeValueMap{
"ManagedEntityId": entityID,
"IntervalEndTime": 0, // This ideally should increment by 1 every collection interval, but staying 0 for simulation is Ok for now.
"ThresholdData12Id": 0,
"TransmittedGemFrames": rand.Intn(100),
"ReceivedGemFrames": rand.Intn(100),
"ReceivedPayloadBytes": rand.Intn(100),
"TransmittedPayloadBytes": rand.Intn(100),
"EncryptionKeyErrors": rand.Intn(100),
},
})
if meErr.GetError() != nil {
omciLogger.Errorf("NewGemPortNetworkCtpPerformanceMonitoringHistoryData %v", meErr.Error())
return nil
}
// L2 PM counters MEs exceed max allowed OMCI payload size.
// So the request/responses are always multipart.
// First identify the attributes that are not requested in the current GET request.
// Then filter out those attributes from the responses in the current GET response.
unwantedAttributeMask := ^attributeMask
var i uint16
for i = 1; i <= 7; i++ { // 1 and 7 because they are allowed valid min and max index keys in AttributeValueMap.
// We leave out 0 because that is ManagedEntity and that is a default IE in the map
if (1<<(7-i))&unwantedAttributeMask > 0 {
if err := managedEntity.DeleteAttributeByIndex(uint(i)); err != nil {
omciLogger.Errorf("error deleting attribute at index=%v, err=%v", i, err)
}
}
}
return &omci.GetResponse{
MeBasePacket: omci.MeBasePacket{
EntityClass: me.GemPortNetworkCtpPerformanceMonitoringHistoryDataClassID,
EntityInstance: entityID,
},
Attributes: managedEntity.GetAttributeValueMap(),
AttributeMask: attributeMask,
Result: me.Success,
}
}
func createOnuDataResponse(attributeMask uint16, entityID uint16, mds uint8) *omci.GetResponse {
managedEntity, meErr := me.NewOnuData(me.ParamData{
EntityID: entityID,
Attributes: me.AttributeValueMap{
"ManagedEntityId": entityID,
"MibDataSync": mds,
},
})
if meErr.GetError() != nil {
omciLogger.Errorf("NewOnuData %v", meErr.Error())
return nil
}
return &omci.GetResponse{
MeBasePacket: omci.MeBasePacket{
EntityClass: me.OnuDataClassID,
EntityInstance: entityID,
},
Attributes: managedEntity.GetAttributeValueMap(),
AttributeMask: attributeMask,
Result: me.Success,
}
}
func createAnigResponse(attributeMask uint16, entityID uint16) *omci.GetResponse {
managedEntity, meErr := me.NewAniG(me.ParamData{
EntityID: entityID,
Attributes: me.AttributeValueMap{
"ManagedEntityId": entityID,
"SrIndication": 0,
"TotalTcontNumber": 0,
"GemBlockLength": 0,
"PiggybackDbaReporting": 0,
"Deprecated": 0,
"SignalFailThreshold": 0,
"SignalDegradeThreshold": 0,
"Arc": 0,
"ArcInterval": 0,
"OpticalSignalLevel": rand.Intn(16000), // generate some random power level than defaulting to 0
"LowerOpticalThreshold": 0,
"UpperOpticalThreshold": 0,
"OnuResponseTime": 0,
"TransmitOpticalLevel": rand.Intn(16000), // generate some random power level than defaulting to 0
"LowerTransmitPowerThreshold": 0,
"UpperTransmitPowerThreshold": 0,
},
})
if meErr.GetError() != nil {
omciLogger.Errorf("NewAniG %v", meErr.Error())
return nil
}
return &omci.GetResponse{
MeBasePacket: omci.MeBasePacket{
EntityClass: me.AniGClassID,
EntityInstance: entityID,
},
Attributes: managedEntity.GetAttributeValueMap(),
AttributeMask: attributeMask,
Result: me.Success,
}
}
func toOctets(str string, size int) []byte {
asciiBytes := []byte(str)
if len(asciiBytes) < size {
missing := size - len(asciiBytes)
for i := 0; i < missing; i++ {
asciiBytes = append(asciiBytes, []byte{0x00}[0])
}
}
return asciiBytes
}