1// 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}
182