blob: 7e74268b7f67d5684d173ede1e136733f998a58b [file] [log] [blame]
Naveen Sampath04696f72022-06-13 15:19:14 +05301// Copyright 2017 Google, Inc. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style license
4// that can be found in the LICENSE file in the root of the source
5// tree.
6//
7
8package layers
9
10import (
11 "encoding/binary"
12 "fmt"
13
14 "github.com/google/gopacket"
15)
16
17const gtpMinimumSizeInBytes int = 8
18
19// GTPExtensionHeader is used to carry extra data and enable future extensions of the GTP without the need to use another version number.
20type GTPExtensionHeader struct {
21 Type uint8
22 Content []byte
23}
24
25// GTPv1U protocol is used to exchange user data over GTP tunnels across the Sx interfaces.
26// Defined in https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=1595
27type GTPv1U struct {
28 BaseLayer
29 Version uint8
30 ProtocolType uint8
31 Reserved uint8
32 ExtensionHeaderFlag bool
33 SequenceNumberFlag bool
34 NPDUFlag bool
35 MessageType uint8
36 MessageLength uint16
37 TEID uint32
38 SequenceNumber uint16
39 NPDU uint8
40 GTPExtensionHeaders []GTPExtensionHeader
41}
42
43// LayerType returns LayerTypeGTPV1U
44func (g *GTPv1U) LayerType() gopacket.LayerType { return LayerTypeGTPv1U }
45
46// DecodeFromBytes analyses a byte slice and attempts to decode it as a GTPv1U packet
47func (g *GTPv1U) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
48 hLen := gtpMinimumSizeInBytes
49 dLen := len(data)
50 if dLen < hLen {
51 return fmt.Errorf("GTP packet too small: %d bytes", dLen)
52 }
53 g.Version = (data[0] >> 5) & 0x07
54 g.ProtocolType = (data[0] >> 4) & 0x01
55 g.Reserved = (data[0] >> 3) & 0x01
56 g.SequenceNumberFlag = ((data[0] >> 1) & 0x01) == 1
57 g.NPDUFlag = (data[0] & 0x01) == 1
58 g.ExtensionHeaderFlag = ((data[0] >> 2) & 0x01) == 1
59 g.MessageType = data[1]
60 g.MessageLength = binary.BigEndian.Uint16(data[2:4])
61 pLen := 8 + g.MessageLength
62 if uint16(dLen) < pLen {
63 return fmt.Errorf("GTP packet too small: %d bytes", dLen)
64 }
65 // Field used to multiplex different connections in the same GTP tunnel.
66 g.TEID = binary.BigEndian.Uint32(data[4:8])
67 cIndex := uint16(hLen)
68 if g.SequenceNumberFlag || g.NPDUFlag || g.ExtensionHeaderFlag {
69 hLen += 4
70 cIndex += 4
71 if dLen < hLen {
72 return fmt.Errorf("GTP packet too small: %d bytes", dLen)
73 }
74 if g.SequenceNumberFlag {
75 g.SequenceNumber = binary.BigEndian.Uint16(data[8:10])
76 }
77 if g.NPDUFlag {
78 g.NPDU = data[10]
79 }
80 if g.ExtensionHeaderFlag {
81 extensionFlag := true
82 for extensionFlag {
83 extensionType := uint8(data[cIndex-1])
84 extensionLength := uint(data[cIndex])
85 if extensionLength == 0 {
86 return fmt.Errorf("GTP packet with invalid extension header")
87 }
88 // extensionLength is in 4-octet units
89 lIndex := cIndex + (uint16(extensionLength) * 4)
90 if uint16(dLen) < lIndex {
91 return fmt.Errorf("GTP packet with small extension header: %d bytes", dLen)
92 }
93 content := data[cIndex+1 : lIndex-1]
94 eh := GTPExtensionHeader{Type: extensionType, Content: content}
95 g.GTPExtensionHeaders = append(g.GTPExtensionHeaders, eh)
96 cIndex = lIndex
97 // Check if coming bytes are from an extension header
98 extensionFlag = data[cIndex-1] != 0
99
100 }
101 }
102 }
103 g.BaseLayer = BaseLayer{Contents: data[:cIndex], Payload: data[cIndex:]}
104 return nil
105
106}
107
108// SerializeTo writes the serialized form of this layer into the
109// SerializationBuffer, implementing gopacket.SerializableLayer.
110// See the docs for gopacket.SerializableLayer for more info.
111func (g *GTPv1U) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
112 data, err := b.PrependBytes(gtpMinimumSizeInBytes)
113 if err != nil {
114 return err
115 }
116 data[0] |= (g.Version << 5)
117 data[0] |= (1 << 4)
118 if len(g.GTPExtensionHeaders) > 0 {
119 data[0] |= 0x04
120 g.ExtensionHeaderFlag = true
121 }
122 if g.SequenceNumberFlag {
123 data[0] |= 0x02
124 }
125 if g.NPDUFlag {
126 data[0] |= 0x01
127 }
128 data[1] = g.MessageType
129 binary.BigEndian.PutUint16(data[2:4], g.MessageLength)
130 binary.BigEndian.PutUint32(data[4:8], g.TEID)
131 if g.ExtensionHeaderFlag || g.SequenceNumberFlag || g.NPDUFlag {
132 data, err := b.AppendBytes(4)
133 if err != nil {
134 return err
135 }
136 binary.BigEndian.PutUint16(data[:2], g.SequenceNumber)
137 data[2] = g.NPDU
138 for _, eh := range g.GTPExtensionHeaders {
139 data[len(data)-1] = eh.Type
140 lContent := len(eh.Content)
141 // extensionLength is in 4-octet units
142 extensionLength := (lContent + 2) / 4
143 // Get two extra byte for the next extension header type and length
144 data, err = b.AppendBytes(lContent + 2)
145 if err != nil {
146 return err
147 }
148 data[0] = byte(extensionLength)
149 copy(data[1:lContent+1], eh.Content)
150 }
151 }
152 return nil
153
154}
155
156// CanDecode returns a set of layers that GTP objects can decode.
157func (g *GTPv1U) CanDecode() gopacket.LayerClass {
158 return LayerTypeGTPv1U
159}
160
161// NextLayerType specifies the next layer that GoPacket should attempt to
162func (g *GTPv1U) NextLayerType() gopacket.LayerType {
163 if len(g.LayerPayload()) == 0 {
164 return gopacket.LayerTypeZero
165 }
166 version := uint8(g.LayerPayload()[0]) >> 4
167 if version == 4 {
168 return LayerTypeIPv4
169 } else if version == 6 {
170 return LayerTypeIPv6
171 } else {
172 return LayerTypePPP
173 }
174}
175
176func decodeGTPv1u(data []byte, p gopacket.PacketBuilder) error {
177 gtp := &GTPv1U{}
178 err := gtp.DecodeFromBytes(data, p)
179 if err != nil {
180 return err
181 }
182 p.AddLayer(gtp)
183 return p.NextDecoder(gtp.NextLayerType())
184}