185 lines
5.3 KiB
Go
185 lines
5.3 KiB
Go
|
// 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"
|
||
|
"fmt"
|
||
|
"github.com/google/gopacket"
|
||
|
)
|
||
|
|
||
|
const gtpMinimumSizeInBytes int = 8
|
||
|
|
||
|
// GTPExtensionHeader is used to carry extra data and enable future extensions of the GTP without the need to use another version number.
|
||
|
type GTPExtensionHeader struct {
|
||
|
Type uint8
|
||
|
Content []byte
|
||
|
}
|
||
|
|
||
|
// GTPv1U protocol is used to exchange user data over GTP tunnels across the Sx interfaces.
|
||
|
// Defined in https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=1595
|
||
|
type GTPv1U struct {
|
||
|
BaseLayer
|
||
|
Version uint8
|
||
|
ProtocolType uint8
|
||
|
Reserved uint8
|
||
|
ExtensionHeaderFlag bool
|
||
|
SequenceNumberFlag bool
|
||
|
NPDUFlag bool
|
||
|
MessageType uint8
|
||
|
MessageLength uint16
|
||
|
TEID uint32
|
||
|
SequenceNumber uint16
|
||
|
NPDU uint8
|
||
|
GTPExtensionHeaders []GTPExtensionHeader
|
||
|
}
|
||
|
|
||
|
// LayerType returns LayerTypeGTPV1U
|
||
|
func (g *GTPv1U) LayerType() gopacket.LayerType { return LayerTypeGTPv1U }
|
||
|
|
||
|
// DecodeFromBytes analyses a byte slice and attempts to decode it as a GTPv1U packet
|
||
|
func (g *GTPv1U) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||
|
hLen := gtpMinimumSizeInBytes
|
||
|
dLen := len(data)
|
||
|
if dLen < hLen {
|
||
|
return fmt.Errorf("GTP packet too small: %d bytes", dLen)
|
||
|
}
|
||
|
g.Version = (data[0] >> 5) & 0x07
|
||
|
g.ProtocolType = (data[0] >> 4) & 0x01
|
||
|
g.Reserved = (data[0] >> 3) & 0x01
|
||
|
g.SequenceNumberFlag = ((data[0] >> 1) & 0x01) == 1
|
||
|
g.NPDUFlag = (data[0] & 0x01) == 1
|
||
|
g.ExtensionHeaderFlag = ((data[0] >> 2) & 0x01) == 1
|
||
|
g.MessageType = data[1]
|
||
|
g.MessageLength = binary.BigEndian.Uint16(data[2:4])
|
||
|
pLen := 8 + g.MessageLength
|
||
|
if uint16(dLen) < pLen {
|
||
|
return fmt.Errorf("GTP packet too small: %d bytes", dLen)
|
||
|
}
|
||
|
// Field used to multiplex different connections in the same GTP tunnel.
|
||
|
g.TEID = binary.BigEndian.Uint32(data[4:8])
|
||
|
cIndex := uint16(hLen)
|
||
|
if g.SequenceNumberFlag || g.NPDUFlag || g.ExtensionHeaderFlag {
|
||
|
hLen += 4
|
||
|
cIndex += 4
|
||
|
if dLen < hLen {
|
||
|
return fmt.Errorf("GTP packet too small: %d bytes", dLen)
|
||
|
}
|
||
|
if g.SequenceNumberFlag {
|
||
|
g.SequenceNumber = binary.BigEndian.Uint16(data[8:10])
|
||
|
}
|
||
|
if g.NPDUFlag {
|
||
|
g.NPDU = data[10]
|
||
|
}
|
||
|
if g.ExtensionHeaderFlag {
|
||
|
extensionFlag := true
|
||
|
for extensionFlag {
|
||
|
extensionType := uint8(data[cIndex-1])
|
||
|
extensionLength := uint(data[cIndex])
|
||
|
if extensionLength == 0 {
|
||
|
return fmt.Errorf("GTP packet with invalid extension header")
|
||
|
}
|
||
|
// extensionLength is in 4-octet units
|
||
|
lIndex := cIndex + (uint16(extensionLength) * 4)
|
||
|
if uint16(dLen) < lIndex {
|
||
|
fmt.Println(dLen, lIndex)
|
||
|
return fmt.Errorf("GTP packet with small extension header: %d bytes", dLen)
|
||
|
}
|
||
|
content := data[cIndex+1 : lIndex-1]
|
||
|
eh := GTPExtensionHeader{Type: extensionType, Content: content}
|
||
|
g.GTPExtensionHeaders = append(g.GTPExtensionHeaders, eh)
|
||
|
cIndex = lIndex
|
||
|
// Check if coming bytes are from an extension header
|
||
|
extensionFlag = data[cIndex-1] != 0
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
g.BaseLayer = BaseLayer{Contents: data[:cIndex], Payload: data[cIndex:]}
|
||
|
return nil
|
||
|
|
||
|
}
|
||
|
|
||
|
// SerializeTo writes the serialized form of this layer into the
|
||
|
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||
|
// See the docs for gopacket.SerializableLayer for more info.
|
||
|
func (g *GTPv1U) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||
|
data, err := b.PrependBytes(gtpMinimumSizeInBytes)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
data[0] |= (g.Version << 5)
|
||
|
data[0] |= (1 << 4)
|
||
|
if len(g.GTPExtensionHeaders) > 0 {
|
||
|
data[0] |= 0x04
|
||
|
g.ExtensionHeaderFlag = true
|
||
|
}
|
||
|
if g.SequenceNumberFlag {
|
||
|
data[0] |= 0x02
|
||
|
}
|
||
|
if g.NPDUFlag {
|
||
|
data[0] |= 0x01
|
||
|
}
|
||
|
data[1] = g.MessageType
|
||
|
binary.BigEndian.PutUint16(data[2:4], g.MessageLength)
|
||
|
binary.BigEndian.PutUint32(data[4:8], g.TEID)
|
||
|
if g.ExtensionHeaderFlag || g.SequenceNumberFlag || g.NPDUFlag {
|
||
|
data, err := b.AppendBytes(4)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
binary.BigEndian.PutUint16(data[:2], g.SequenceNumber)
|
||
|
data[2] = g.NPDU
|
||
|
for _, eh := range g.GTPExtensionHeaders {
|
||
|
data[len(data)-1] = eh.Type
|
||
|
lContent := len(eh.Content)
|
||
|
// extensionLength is in 4-octet units
|
||
|
extensionLength := (lContent + 2) / 4
|
||
|
// Get two extra byte for the next extension header type and length
|
||
|
data, err = b.AppendBytes(lContent + 2)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
data[0] = byte(extensionLength)
|
||
|
copy(data[1:lContent+1], eh.Content)
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
|
||
|
}
|
||
|
|
||
|
// CanDecode returns a set of layers that GTP objects can decode.
|
||
|
func (g *GTPv1U) CanDecode() gopacket.LayerClass {
|
||
|
return LayerTypeGTPv1U
|
||
|
}
|
||
|
|
||
|
// NextLayerType specifies the next layer that GoPacket should attempt to
|
||
|
func (g *GTPv1U) NextLayerType() gopacket.LayerType {
|
||
|
if len(g.LayerPayload()) == 0 {
|
||
|
return gopacket.LayerTypeZero
|
||
|
}
|
||
|
version := uint8(g.LayerPayload()[0]) >> 4
|
||
|
if version == 4 {
|
||
|
return LayerTypeIPv4
|
||
|
} else if version == 6 {
|
||
|
return LayerTypeIPv6
|
||
|
} else {
|
||
|
return LayerTypePPP
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func decodeGTPv1u(data []byte, p gopacket.PacketBuilder) error {
|
||
|
gtp := >Pv1U{}
|
||
|
err := gtp.DecodeFromBytes(data, p)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
p.AddLayer(gtp)
|
||
|
return p.NextDecoder(gtp.NextLayerType())
|
||
|
}
|