ptp/protocol/management.go (233 lines of code) (raw):
/*
Copyright (c) Facebook, Inc. and its affiliates.
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 protocol
import (
"bytes"
"encoding"
"encoding/binary"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
)
var identity PortIdentity
// ErrManagementMsgErrorStatus is what happens if we expected to get Management TLV in response, but received special ManagementErrorStatusTLV
var ErrManagementMsgErrorStatus = errors.New("received MANAGEMENT_ERROR_STATUS_TLV")
func init() {
// store our PID as identity that we use to talk to ptp daemon
identity.PortNumber = uint16(os.Getpid())
}
// Action indicate the action to be taken on receipt of the PTP message as defined in Table 57
type Action uint8
// actions as in Table 57 Values of the actionField
const (
GET Action = iota
SET
RESPONSE
COMMAND
ACKNOWLEDGE
)
// ManagementTLVHead Spec Table 58 - Management TLV fields
type ManagementTLVHead struct {
TLVHead
ManagementID ManagementID
}
// ManagementMsgHead Spec Table 56 - Management message fields
type ManagementMsgHead struct {
Header
TargetPortIdentity PortIdentity
StartingBoundaryHops uint8
BoundaryHops uint8
ActionField Action
Reserved uint8
}
// Action returns ActionField
func (p *ManagementMsgHead) Action() Action {
return p.ActionField
}
// MgmtID returns ManagementID
func (p *ManagementTLVHead) MgmtID() ManagementID {
return p.ManagementID
}
// Management packet, see '15. PTP management messages'
type Management struct {
ManagementMsgHead
TLV ManagementTLV
}
// UnmarshalBinary parses []byte and populates struct fields
func (p *Management) UnmarshalBinary(rawBytes []byte) error {
var err error
head := ManagementMsgHead{}
tlvHead := ManagementTLVHead{}
r := bytes.NewReader(rawBytes)
if err = binary.Read(r, binary.BigEndian, &head); err != nil {
return err
}
if err = binary.Read(r, binary.BigEndian, &tlvHead.TLVHead); err != nil {
return err
}
if tlvHead.TLVType == TLVManagementErrorStatus {
return ErrManagementMsgErrorStatus
}
if tlvHead.TLVType != TLVManagement {
return fmt.Errorf("got TLV type 0x%x instead of 0x%x", tlvHead.TLVType, TLVManagement)
}
if err = binary.Read(r, binary.BigEndian, &tlvHead.ManagementID); err != nil {
return err
}
headSize := binary.Size(tlvHead)
// seek back so we can read whole TLV
if _, err := r.Seek(-int64(headSize), io.SeekCurrent); err != nil {
return err
}
decoder, found := mgmtTLVDecoder[tlvHead.ManagementID]
if !found {
return fmt.Errorf("unsupported management TLV 0x%x", tlvHead.ManagementID)
}
tlvData, err := ioutil.ReadAll(r)
if err != nil {
return err
}
tlv, err := decoder(tlvData)
if err != nil {
return err
}
p.ManagementMsgHead = head
p.TLV = tlv
return nil
}
// MarshalBinaryTo converts packet to bytes and writes those into provided buffer
func (p *Management) MarshalBinaryTo(bytes io.Writer) error {
if err := binary.Write(bytes, binary.BigEndian, p.ManagementMsgHead); err != nil {
return err
}
// interface smuggling
if pp, ok := p.TLV.(encoding.BinaryMarshaler); ok {
b, err := pp.MarshalBinary()
if err != nil {
return err
}
return binary.Write(bytes, binary.BigEndian, b)
}
if err := binary.Write(bytes, binary.BigEndian, p.TLV); err != nil {
return err
}
return nil
}
// MarshalBinary converts packet to []bytes
func (p *Management) MarshalBinary() ([]byte, error) {
var bytes bytes.Buffer
err := p.MarshalBinaryTo(&bytes)
return bytes.Bytes(), err
}
// ManagementErrorStatusTLV spec Table 108 MANAGEMENT_ERROR_STATUS TLV format
type ManagementErrorStatusTLV struct {
TLVHead
ManagementErrorID ManagementErrorID
ManagementID ManagementID
Reserved int32
DisplayData PTPText
}
// ManagementMsgErrorStatus is header + ManagementErrorStatusTLV
type ManagementMsgErrorStatus struct {
ManagementMsgHead
ManagementErrorStatusTLV
}
// UnmarshalBinary parses []byte and populates struct fields
func (p *ManagementMsgErrorStatus) UnmarshalBinary(rawBytes []byte) error {
reader := bytes.NewReader(rawBytes)
be := binary.BigEndian
if err := binary.Read(reader, be, &p.ManagementMsgHead); err != nil {
return fmt.Errorf("reading ManagementMsgErrorStatus ManagementMsgHead: %w", err)
}
if err := binary.Read(reader, be, &p.ManagementErrorStatusTLV.TLVHead); err != nil {
return fmt.Errorf("reading ManagementMsgErrorStatus TLVHead: %w", err)
}
if err := binary.Read(reader, be, &p.ManagementErrorStatusTLV.ManagementErrorID); err != nil {
return fmt.Errorf("reading ManagementMsgErrorStatus ManagementErrorID: %w", err)
}
if err := binary.Read(reader, be, &p.ManagementErrorStatusTLV.ManagementID); err != nil {
return fmt.Errorf("reading ManagementMsgErrorStatus ManagementID: %w", err)
}
if err := binary.Read(reader, be, &p.ManagementErrorStatusTLV.Reserved); err != nil {
return fmt.Errorf("reading ManagementMsgErrorStatus Reserved: %w", err)
}
// packet can have trailing bytes, let's make sure we don't try to read past given length
toRead := int(p.ManagementMsgHead.Header.MessageLength)
toRead -= binary.Size(p.ManagementMsgHead)
toRead -= binary.Size(p.ManagementErrorStatusTLV.TLVHead)
toRead -= binary.Size(p.ManagementErrorStatusTLV.ManagementErrorID)
toRead -= binary.Size(p.ManagementErrorStatusTLV.ManagementID)
toRead -= binary.Size(p.ManagementErrorStatusTLV.Reserved)
if reader.Len() == 0 || toRead <= 0 {
// DisplayData is completely optional
return nil
}
data := make([]byte, reader.Len())
if _, err := io.ReadFull(reader, data); err != nil {
return err
}
if err := p.DisplayData.UnmarshalBinary(data); err != nil {
return fmt.Errorf("reading ManagementMsgErrorStatus DisplayData: %w", err)
}
return nil
}
// MarshalBinaryTo converts packet to bytes and writes those into provided buffer
func (p *ManagementMsgErrorStatus) MarshalBinaryTo(bytes io.Writer) error {
be := binary.BigEndian
if err := binary.Write(bytes, be, &p.ManagementMsgHead); err != nil {
return fmt.Errorf("writing ManagementMsgErrorStatus ManagementMsgHead: %w", err)
}
if err := binary.Write(bytes, be, &p.ManagementErrorStatusTLV.TLVHead); err != nil {
return fmt.Errorf("writing ManagementMsgErrorStatus TLVHead: %w", err)
}
if err := binary.Write(bytes, be, &p.ManagementErrorStatusTLV.ManagementErrorID); err != nil {
return fmt.Errorf("writing ManagementMsgErrorStatus ManagementErrorID: %w", err)
}
if err := binary.Write(bytes, be, &p.ManagementErrorStatusTLV.ManagementID); err != nil {
return fmt.Errorf("writing ManagementMsgErrorStatus ManagementID: %w", err)
}
if err := binary.Write(bytes, be, &p.ManagementErrorStatusTLV.Reserved); err != nil {
return fmt.Errorf("writing ManagementMsgErrorStatus Reserved: %w", err)
}
if p.DisplayData != "" {
dd, err := p.DisplayData.MarshalBinary()
if err != nil {
return fmt.Errorf("writing ManagementMsgErrorStatus DisplayData: %w", err)
}
if _, err := bytes.Write(dd); err != nil {
return err
}
}
return nil
}
// MarshalBinary converts packet to []bytes
func (p *ManagementMsgErrorStatus) MarshalBinary() ([]byte, error) {
var bytes bytes.Buffer
err := p.MarshalBinaryTo(&bytes)
return bytes.Bytes(), err
}
// ManagementErrorID is an enum for possible management errors
type ManagementErrorID uint16
// Table 109 ManagementErrorID enumeration
const (
ErrorResponseTooBig ManagementErrorID = 0x0001 // The requested operation could not fit in a single response message
ErrorNoSuchID ManagementErrorID = 0x0002 // The managementId is not recognized
ErrorWrongLength ManagementErrorID = 0x0003 // The managementId was identified but the length of the data was wrong
ErrorWrongValue ManagementErrorID = 0x0004 // The managementId and length were correct but one or more values were wrong
ErrorNotSetable ManagementErrorID = 0x0005 // Some of the variables in the set command were not updated because they are not configurable
ErrorNotSupported ManagementErrorID = 0x0006 // The requested operation is not supported in this PTP Instance
ErrorUnpopulated ManagementErrorID = 0x0007 // The targetPortIdentity of the PTP management message refers to an entity that is not present in the PTP Instance at the time of the request
// some reserved and provile-specific ranges
ErrorGeneralError ManagementErrorID = 0xFFFE //An error occurred that is not covered by other ManagementErrorID values
)
// ManagementErrorIDToString is a map from ManagementErrorID to string
var ManagementErrorIDToString = map[ManagementErrorID]string{
ErrorResponseTooBig: "RESPONSE_TOO_BIG",
ErrorNoSuchID: "NO_SUCH_ID",
ErrorWrongLength: "WRONG_LENGTH",
ErrorWrongValue: "WRONG_VALUE",
ErrorNotSetable: "NOT_SETABLE",
ErrorNotSupported: "NOT_SUPPORTED",
ErrorUnpopulated: "UNPOPULATED",
ErrorGeneralError: "GENERAL_ERROR",
}
func (t ManagementErrorID) String() string {
s := ManagementErrorIDToString[t]
if s == "" {
return fmt.Sprintf("UNKNOWN_ERROR_ID=%d", t)
}
return s
}
func (t ManagementErrorID) Error() string {
return t.String()
}
func decodeMgmtPacket(data []byte) (Packet, error) {
packet := &Management{}
err := packet.UnmarshalBinary(data)
if errors.Is(err, ErrManagementMsgErrorStatus) {
errorPacket := new(ManagementMsgErrorStatus)
if err := errorPacket.UnmarshalBinary(data); err != nil {
return nil, fmt.Errorf("got Management Error in response but failed to decode it: %w", err)
}
return errorPacket, nil
}
if err != nil {
return nil, err
}
return packet, nil
}