File: gtp.go

package info (click to toggle)
golang-github-gopacket-gopacket 1.3.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 6,004 kB
  • sloc: sh: 301; python: 76; makefile: 10
file content (184 lines) | stat: -rw-r--r-- 5,401 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// 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/gopacket/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 {
					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 := &GTPv1U{}
	err := gtp.DecodeFromBytes(data, p)
	if err != nil {
		return err
	}
	p.AddLayer(gtp)
	return p.NextDecoder(gtp.NextLayerType())
}