// Copyright 2025 Google, Inc. All rights reserved.
// Copyright 2009-2011 Andreas Krennmair. 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 TestApspDecoding(t *testing.T) {
	// This is a packet generated by Andromeda USPS, captured by etcpdump. It includes:
	// - Ethernet
	// - IPv6
	// - UDP (port 1000)
	// - APSP with Security Token and Endpoint IDs
	// - IPv4
	// - UDP (stripped)
	// from gpowersx:~/etcpdump-fixlen/bgbfj25-nic.pcap, wireshark export packet dissections as C arrays
	// Inner UDP header/data is stripped, but IP length is not adjusted.
	ph := `14223bae76fa02320000000086dd66e000000101116420020a0566926d0f000000000000000020020a056f00a419000000000000000078a303e80101000004040b030005ea7f0aff5375f54bc800a68372eac001100300bf39b40a0a0c0e00bf39b40a0a0cee450000c112f540003f11a9eb0a1923090a194711`
	pr, err := hex.DecodeString(ph)
	if err != nil {
		t.Errorf("Error decoding hex packet: %v", err)
	}

	p := gopacket.NewPacket(pr, LayerTypeEthernet, gopacket.Default)
	if p.ErrorLayer() != nil {
		t.Errorf("APSP decoding error: %v", p.ErrorLayer().Error())
	}

	apsp := p.Layer(LayerTypeAPSP).(APSP)
	if uint8(4) != apsp.NextHeader {
		t.Errorf("apsp.NextHeader = %v, want 4", apsp.NextHeader)
	}
}

// This is a made-up packet derived from a different encap example.
var testIPv4OverAPSP = []gopacket.SerializableLayer{
	&Ethernet{
		SrcMAC:       net.HardwareAddr{142, 122, 18, 195, 169, 113},
		DstMAC:       net.HardwareAddr{58, 86, 107, 105, 89, 94},
		EthernetType: EthernetTypeIPv4,
	}, // len=14, total length should be 152 + 14 = 166
	&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,
	}, // len=20, IP length field should be 20 + 132 = 152
	&UDP{
		SrcPort: 8,
		DstPort: 1000,
	}, // len=8, UDP length field should be 8 + 40 + 20 + 8 + 56 = 132
	&APSP{
		NextHeader:    4,                  // IPv4
		HdrExtLen:     4,                  // endpoint IDs
		CryptOffset:   11,                 // following the APSP and IPv4 headers
		SDVersVirt:    3,                  // SD=1, Virt=0, Vers=1
		SecParamIdx:   0x0005ea7f,         // taken from sample packet
		InitVector:    0x0aff5375f54bc800, // taken from sample packet
		SecTokenV2:    0xa68372ea,         // taken from sample packet
		VirtKey:       0xc0011003,         // taken from sample packet
		SrcEndpointID: 0x00bf39b40a0a0c0e, // taken from sample packet
		DstEndpointID: 0x00bf39b40a0a0cee, // taken from sample packet
	}, // len=40, APSP total length should be 40 + 84 = 124
	&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,
	}, // len=20, IPv4 length field should be 20 + 8 + 56 = 84
	&ICMPv4{
		TypeCode: CreateICMPv4TypeCode(ICMPv4TypeEchoRequest, 0),
		Id:       4724,
		Seq:      1,
	}, // len=8, ICMP length field should be 8 + 56 = 64
	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,
	}, // len=56
}

func TestIPv4OverApspEncode(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, testIPv4OverAPSP...); 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, LayerTypeAPSP,
		LayerTypeIPv4, LayerTypeICMPv4, gopacket.LayerTypePayload}, t)

	layers := p.Layers()
	etherLayer := p.Layer(LayerTypeEthernet)
	ether := Ethernet{}
	ether.DecodeFromBytes(etherLayer.LayerContents(), gopacket.NilDecodeFeedback)
	if ether.EthernetType != EthernetTypeIPv4 {
		t.Errorf("Ethernet type is %v, not %v", ether.EthernetType, EthernetTypeIPv4)
	}
	ipLayer := p.Layer(LayerTypeIPv4)
	ip1 := IPv4{}
	ip1.DecodeFromBytes(ipLayer.LayerContents(), gopacket.NilDecodeFeedback)
	if ip1.Length != 152 {
		t.Errorf("inner IPv4 length is %d, not 152", ip1.Length)
	}
	udpLayer := p.Layer(LayerTypeUDP)
	udp1 := UDP{}
	udp1.DecodeFromBytes(udpLayer.LayerContents(), gopacket.NilDecodeFeedback)
	if udp1.Length != 132 {
		t.Errorf("outer UDP length is %d, not 132", udp1.Length)
	}
	apsp1 := p.Layer(LayerTypeAPSP).(APSP)
	if apsp1.CryptOffset != 11 {
		t.Errorf("APSP crypt offset is %d, not 11", apsp1.CryptOffset)
	}
	ipLayer = layers[4]
	ip2 := IPv4{}
	ip2.DecodeFromBytes(ipLayer.LayerContents(), gopacket.NilDecodeFeedback)
	if ip2.Length != 84 {
		t.Errorf("inner IPv4 length is %d, not 84", ip2.Length)
	}
	icmpLayer := p.Layer(LayerTypeICMPv4)
	icmp := ICMPv4{}
	icmp.DecodeFromBytes(icmpLayer.LayerContents(), gopacket.NilDecodeFeedback)
	if icmp.TypeCode != 0x800 {
		t.Errorf("ICMP type is %x, not %x", icmp.TypeCode, 0x800)
	}

	checkSerialization(p, t)
}
