blob: 0ec8a6a0020287efbf9b60890eb1be63f966eb50 [file] [log] [blame]
Holger Hildebrandtfa074992020-03-27 15:42:06 +00001// 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 "github.com/google/gopacket"
14)
15
16const gtpMinimumSizeInBytes int = 8
17
18// GTPExtensionHeader is used to carry extra data and enable future extensions of the GTP without the need to use another version number.
19type GTPExtensionHeader struct {
20 Type uint8
21 Content []byte
22}
23
24// GTPv1U protocol is used to exchange user data over GTP tunnels across the Sx interfaces.
25// Defined in https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=1595
26type GTPv1U struct {
27 BaseLayer
28 Version uint8
29 ProtocolType uint8
30 Reserved uint8
31 ExtensionHeaderFlag bool
32 SequenceNumberFlag bool
33 NPDUFlag bool
34 MessageType uint8
35 MessageLength uint16
36 TEID uint32
37 SequenceNumber uint16
38 NPDU uint8
39 GTPExtensionHeaders []GTPExtensionHeader
40}
41
42// LayerType returns LayerTypeGTPV1U
43func (g *GTPv1U) LayerType() gopacket.LayerType { return LayerTypeGTPv1U }
44
45// DecodeFromBytes analyses a byte slice and attempts to decode it as a GTPv1U packet
46func (g *GTPv1U) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
47 hLen := gtpMinimumSizeInBytes
48 dLen := len(data)
49 if dLen < hLen {
50 return fmt.Errorf("GTP packet too small: %d bytes", dLen)
51 }
52 g.Version = (data[0] >> 5) & 0x07
53 g.ProtocolType = (data[0] >> 4) & 0x01
54 g.Reserved = (data[0] >> 3) & 0x01
55 g.SequenceNumberFlag = ((data[0] >> 1) & 0x01) == 1
56 g.NPDUFlag = (data[0] & 0x01) == 1
57 g.ExtensionHeaderFlag = ((data[0] >> 2) & 0x01) == 1
58 g.MessageType = data[1]
59 g.MessageLength = binary.BigEndian.Uint16(data[2:4])
60 pLen := 8 + g.MessageLength
61 if uint16(dLen) < pLen {
62 return fmt.Errorf("GTP packet too small: %d bytes", dLen)
63 }
64 // Field used to multiplex different connections in the same GTP tunnel.
65 g.TEID = binary.BigEndian.Uint32(data[4:8])
66 cIndex := uint16(hLen)
67 if g.SequenceNumberFlag || g.NPDUFlag || g.ExtensionHeaderFlag {
68 hLen += 4
69 cIndex += 4
70 if dLen < hLen {
71 return fmt.Errorf("GTP packet too small: %d bytes", dLen)
72 }
73 if g.SequenceNumberFlag {
74 g.SequenceNumber = binary.BigEndian.Uint16(data[8:10])
75 }
76 if g.NPDUFlag {
77 g.NPDU = data[10]
78 }
79 if g.ExtensionHeaderFlag {
80 extensionFlag := true
81 for extensionFlag {
82 extensionType := uint8(data[cIndex-1])
83 extensionLength := uint(data[cIndex])
84 if extensionLength == 0 {
85 return fmt.Errorf("GTP packet with invalid extension header")
86 }
87 // extensionLength is in 4-octet units
88 lIndex := cIndex + (uint16(extensionLength) * 4)
89 if uint16(dLen) < lIndex {
90 fmt.Println(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 version := uint8(g.LayerPayload()[0]) >> 4
164 if version == 4 {
165 return LayerTypeIPv4
166 } else if version == 6 {
167 return LayerTypeIPv6
168 } else {
169 return LayerTypePPP
170 }
171}
172
173func decodeGTPv1u(data []byte, p gopacket.PacketBuilder) error {
174 gtp := &GTPv1U{}
175 err := gtp.DecodeFromBytes(data, p)
176 if err != nil {
177 return err
178 }
179 p.AddLayer(gtp)
180 return p.NextDecoder(gtp.NextLayerType())
181}