blob: 0ec8a6a0020287efbf9b60890eb1be63f966eb50 [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"
"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 {
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 := &GTPv1U{}
err := gtp.DecodeFromBytes(data, p)
if err != nil {
return err
}
p.AddLayer(gtp)
return p.NextDecoder(gtp.NextLayerType())
}