blob: 31c2a6ea16d3a1ab03d41bbb1728466249eb5dc6 [file] [log] [blame]
// Copyright 2017 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"errors"
"fmt"
"net"
"github.com/google/gopacket"
)
type STPSwitchID struct {
Priority uint16 // Bridge priority
SysID uint16 // VLAN ID
HwAddr net.HardwareAddr
}
// STP decode spanning tree protocol packets to transport BPDU (bridge protocol data unit) message.
type STP struct {
BaseLayer
ProtocolID uint16
Version uint8
Type uint8
TC, TCA bool // TC: Topologie change ; TCA: Topologie change ack
RouteID, BridgeID STPSwitchID
Cost uint32
PortID uint16
MessageAge uint16
MaxAge uint16
HelloTime uint16
FDelay uint16
}
// LayerType returns gopacket.LayerTypeSTP.
func (s *STP) LayerType() gopacket.LayerType { return LayerTypeSTP }
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (s *STP) CanDecode() gopacket.LayerClass {
return LayerTypeSTP
}
// DecodeFromBytes decodes the given bytes into this layer.
func (stp *STP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
stpLength := 35
if len(data) < stpLength {
df.SetTruncated()
return fmt.Errorf("STP length %d too short", len(data))
}
stp.ProtocolID = binary.BigEndian.Uint16(data[:2])
stp.Version = uint8(data[2])
stp.Type = uint8(data[3])
stp.TC = data[4]&0x01 != 0
stp.TCA = data[4]&0x80 != 0
stp.RouteID.Priority = binary.BigEndian.Uint16(data[5:7]) & 0xf000
stp.RouteID.SysID = binary.BigEndian.Uint16(data[5:7]) & 0x0fff
stp.RouteID.HwAddr = net.HardwareAddr(data[7:13])
stp.Cost = binary.BigEndian.Uint32(data[13:17])
stp.BridgeID.Priority = binary.BigEndian.Uint16(data[17:19]) & 0xf000
stp.BridgeID.SysID = binary.BigEndian.Uint16(data[17:19]) & 0x0fff
stp.BridgeID.HwAddr = net.HardwareAddr(data[19:25])
stp.PortID = binary.BigEndian.Uint16(data[25:27])
stp.MessageAge = binary.BigEndian.Uint16(data[27:29])
stp.MaxAge = binary.BigEndian.Uint16(data[29:31])
stp.HelloTime = binary.BigEndian.Uint16(data[31:33])
stp.FDelay = binary.BigEndian.Uint16(data[33:35])
stp.Contents = data[:stpLength]
stp.Payload = data[stpLength:]
return nil
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (stp *STP) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
// Check if the priority value is correct.
func checkPriority(prio uint16) (uint16, error) {
if prio == 0 {
return prio, errors.New("Invalid Priority value must be in the rage <4096-61440> with an increment of 4096")
}
if prio%4096 == 0 {
return prio, nil
} else {
return prio, errors.New("Invalid Priority value must be in the rage <4096-61440> with an increment of 4096")
}
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (s *STP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
var flags uint8 = 0x00
bytes, err := b.PrependBytes(35)
if err != nil {
return err
}
binary.BigEndian.PutUint16(bytes, s.ProtocolID)
bytes[2] = s.Version
bytes[3] = s.Type
if s.TC {
flags |= 0x01
}
if s.TCA {
flags |= 0x80
}
bytes[4] = flags
prioRoot, err := checkPriority(s.RouteID.Priority)
if err != nil {
panic(err)
}
if s.RouteID.SysID >= 4096 {
panic("Invalid VlanID value ..!")
}
binary.BigEndian.PutUint16(bytes[5:7], prioRoot|s.RouteID.SysID)
copy(bytes[7:13], s.RouteID.HwAddr)
binary.BigEndian.PutUint32(bytes[13:17], s.Cost)
prioBridge, err := checkPriority(s.BridgeID.Priority)
if err != nil {
panic(err)
}
if s.BridgeID.SysID >= 4096 {
panic("Invalid VlanID value ..!")
}
binary.BigEndian.PutUint16(bytes[17:19], prioBridge|s.BridgeID.SysID)
copy(bytes[19:25], s.BridgeID.HwAddr)
binary.BigEndian.PutUint16(bytes[25:27], s.PortID)
binary.BigEndian.PutUint16(bytes[27:29], s.MessageAge)
binary.BigEndian.PutUint16(bytes[29:31], s.MaxAge)
binary.BigEndian.PutUint16(bytes[31:33], s.HelloTime)
binary.BigEndian.PutUint16(bytes[33:35], s.FDelay)
return nil
}
func decodeSTP(data []byte, p gopacket.PacketBuilder) error {
stp := &STP{}
return decodingLayerDecoder(stp, data, p)
}