Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions layers/ague_var0.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright 2025 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.
// Copyright 2025 The GoPacket Authors. 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 (
"errors"

"github.com/gopacket/gopacket"
)

// AGUEVar0 represents a packet encoded with Generic UDP Encapsulation.
// It should sit "under" a UDP layer with dest port 666.
//
// For more information about the meaning of the fields, see
// https://tools.ietf.org/html/draft-ietf-intarea-gue-04#section-3.1
type AGUEVar0 struct {
Version uint8
C bool
Protocol IPProtocol
Flags uint16
Extensions []byte
Data []byte
}

// LayerType returns this pseudo-header's type as defined in layertypes.go
func (l AGUEVar0) LayerType() gopacket.LayerType {
return LayerTypeAGUEVar0
}

// LayerContents returns a byte array containing our serialized header.
func (l AGUEVar0) LayerContents() []byte {
b := make([]byte, 4, 4+len(l.Extensions))
hlen := uint8(len(l.Extensions))
b[0] = l.Version<<6 | hlen
if l.C {
b[0] |= 0x20
}
b[0] |= hlen
b[1] = byte(l.Protocol)
b[2] = byte(l.Flags >> 8)
b[3] = byte(l.Flags & 0xff)
b = append(b, l.Extensions...)
return b
}

// LayerPayload returns an IPv4 or IPv6 packet in serialized form.
func (l AGUEVar0) LayerPayload() []byte {
return l.Data
}

// SerializeTo writes our header into SerializeBuffer.
func (l AGUEVar0) SerializeTo(buf gopacket.SerializeBuffer, _ gopacket.SerializeOptions) error {
b := l.LayerContents()
writeTo, err := buf.PrependBytes(len(b))
if err != nil {
return err
}
copy(writeTo, b)
return nil
}

// CanDecode returns the type of layer we can decode.
func (l AGUEVar0) CanDecode() gopacket.LayerClass {
return LayerTypeAGUEVar0
}

// DecodeFromBytes extracts our header data from a serialized packet.
func (l *AGUEVar0) DecodeFromBytes(data []byte, _ gopacket.DecodeFeedback) error {
l.Version = data[0] >> 6
l.C = data[0]&0x20 != 0
l.Protocol = IPProtocol(data[1])
l.Flags = (uint16(data[2]) << 8) | uint16(data[3])
hlen := data[0] & 0x1f
l.Extensions = data[4 : 4+hlen]
l.Data = data[4+hlen:]
return nil
}

// NextLayerType returns the next layer type, e.g. LayerTypeIPv4
func (l AGUEVar0) NextLayerType() gopacket.LayerType {
return l.Protocol.LayerType()
}

// decodeAGUE decodes AGUEVar0 or AGUEVar1, depending on the first data byte.
// If AGUEVar1, it refers the packet to AGUEVar1 for decoding.
// Else it adds AGUEVar0 layer info to the packet object, recursively decodes
// remaining layers, and returns the next-layer type (IPv4 or IPv6).
func decodeAGUE(data []byte, p gopacket.PacketBuilder) error {
if len(data) == 0 {
return errors.New("decodeAGUE() failed, no data")
}
if data[0]>>6 == 1 {
return decodeAGUEVar1(data, p)
}
l := AGUEVar0{}
if err := l.DecodeFromBytes(data, gopacket.NilDecodeFeedback); err != nil {
return err
}
p.AddLayer(l)
return p.NextDecoder(l.NextLayerType())
}
112 changes: 112 additions & 0 deletions layers/ague_var0_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright 2025 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.
// Copyright 2025 The GoPacket Authors. 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/hex"
"net"
"testing"

"github.com/gopacket/gopacket"
)

func TestGueDecoding(t *testing.T) {
// This is a packet generated by the Linux kernel GUE implementation, captured
// by pcap. It includes:
// - Ethernet
// - IPv4
// - UDP
// - AGueVar0 (port 666)
// - IPv4
// - ICMP (ping)
// TODO: build a test packet using port 666 (0x029a)
ph := `02427b2522f502420ae0d90608004500007451ea4000ff119d050ae0d9060afa9da88c0f029a00608cfa000400004500005459f240004001e2cd0ae0d9060afd0f0608000a7e005f000cea811f59000000005daa080000000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627`
pr, err := hex.DecodeString(ph)
if err != nil {
t.Errorf("Error decoding hex packet: %v", err)
}
p := gopacket.NewPacket(pr, LayerTypeEthernet, gopacket.Default)
// require.Nil(t, p.ErrorLayer())
if p.ErrorLayer() != nil {
t.Errorf("AGUEVar0 layer is nil")
}
t.Logf("%v", p)
gue := p.Layer(LayerTypeAGUEVar0).(AGUEVar0)
// require.NotNil(t, gue)
if uint8(0) != gue.Version {
t.Errorf("gue.Version is not 0")
}
}

var testIPv4OverAGUEVar0 = []gopacket.SerializableLayer{
&Ethernet{
SrcMAC: net.HardwareAddr{142, 122, 18, 195, 169, 113},
DstMAC: net.HardwareAddr{58, 86, 107, 105, 89, 94},
EthernetType: EthernetTypeIPv4,
},
&IPv4{
Version: 4,
SrcIP: net.IP{192, 168, 1, 1},
DstIP: net.IP{192, 168, 1, 2},
Protocol: IPProtocolUDP,
Flags: IPv4DontFragment,
TTL: 64,
Id: 33852,
IHL: 5,
},
&UDP{
SrcPort: 8,
DstPort: 666,
},
&AGUEVar0{
Protocol: IPProtocolIPv4,
},
&IPv4{
Version: 4,
SrcIP: net.IP{172, 16, 1, 1},
DstIP: net.IP{172, 16, 2, 1},
Protocol: IPProtocolICMPv4,
Flags: IPv4DontFragment,
TTL: 64,
IHL: 5,
Id: 1160,
},
&ICMPv4{
TypeCode: CreateICMPv4TypeCode(ICMPv4TypeEchoRequest, 0),
Id: 4724,
Seq: 1,
},
gopacket.Payload{
0xc8, 0x92, 0xa3, 0x54, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
},
}

func TestIPv4OverAGUEVar0Encode(t *testing.T) {
b := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{
ComputeChecksums: false, // if desired, see gre_test:setNetworkLayer()
FixLengths: true,
}
if err := gopacket.SerializeLayers(b, opts, testIPv4OverAGUEVar0...); err != nil {
t.Errorf("Unable to serialize: %v", err)
}
p := gopacket.NewPacket(b.Bytes(), LinkTypeEthernet, gopacket.Default)
if p.ErrorLayer() != nil {
t.Error("Failed to decode packet:", p.ErrorLayer().Error())
}
checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeUDP, LayerTypeAGUEVar0, LayerTypeIPv4, LayerTypeICMPv4, gopacket.LayerTypePayload}, t)
// We don't have a corresponding sample packet capture, but if we did, the verify would look like this:
// if got, want := b.Bytes(), testPacketAGUEVar0``; !reflect.DeepEqual(want, got) {
// t.Errorf("Encoding mismatch, \nwant: %v\ngot %v\n", want, got)
// }
}
87 changes: 87 additions & 0 deletions layers/ague_var1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright 2025 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.
// Copyright 2025 The GoPacket Authors. 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 (
"errors"

"github.com/gopacket/gopacket"
)

// An AGUEVar1 header is mostly imaginary, having a length of 0 in its serialized form.
// IPProtocol value is either IPProtocolIPv4 or IPProtocolIPv6, depending on the encapped
// IP header contained in Data, which must begin with the high-order bits 01.
type AGUEVar1 struct {
Protocol IPProtocol
Data []byte
}

// LayerType returns this pseudo-header's type as defined in layertypes.go
func (l AGUEVar1) LayerType() gopacket.LayerType {
return LayerTypeAGUEVar1
}

// LayerContents returns an empty byte array, because this header has no length.
func (l AGUEVar1) LayerContents() []byte {
b := make([]byte, 0)
return b
}

// LayerPayload returns an IPv4 or IPv6 packet in serialized form.
func (l AGUEVar1) LayerPayload() []byte {
return l.Data
}

// SerializeTo writes our imaginary header into SerializeBuffer. This amount to a noop.
func (l AGUEVar1) SerializeTo(_ gopacket.SerializeBuffer, _ gopacket.SerializeOptions) error {
return nil
}

// CanDecode returns the type of layer we can decode.
func (l AGUEVar1) CanDecode() gopacket.LayerClass {
return LayerTypeAGUEVar1
}

// DecodeFromBytes extracts our pseudo-header data from a serialized packet.
// There's only one thing, the next header type, which is either IPv4 or IPv6.
// They are crafted to keep their own header type in the first nibble.
// So we peek into the IP header to get the next-layer protocol type.
func (l *AGUEVar1) DecodeFromBytes(data []byte, _ gopacket.DecodeFeedback) error {
if len(data) < 1 {
return errors.New("DecodeFromBytes() failed, no data")
}
ipVersion := data[0] >> 4
if ipVersion == 4 {
l.Protocol = IPProtocolIPv4
} else if ipVersion == 6 {
l.Protocol = IPProtocolIPv6
} else {
return errors.New("DecodeFromBytes() failed, unknown IP version")
}
l.Data = data
return nil
}

// NextLayerType returns the next layer type, e.g. LayerTypeIPv4
func (l AGUEVar1) NextLayerType() gopacket.LayerType {
return l.Protocol.LayerType()
}

// decodeAGUEVar1 decodes packet data to figure out the next-layer IP type,
// then adds AGUEVar1 layer info to the packet object, recursively decodes
// remaining layers, and returns the next-layer type.
func decodeAGUEVar1(data []byte, p gopacket.PacketBuilder) error {
l := AGUEVar1{}
if err := l.DecodeFromBytes(data, gopacket.NilDecodeFeedback); err != nil {
return err
}
p.AddLayer(l)
return p.NextDecoder(l.NextLayerType())
}
Loading
Loading