mirror of
https://github.com/tailscale/tailscale.git
synced 2024-12-03 23:15:42 +00:00
cb96b14bf4
Upstream netaddr has a change that makes it alloc-free, so it's safe to use in hot codepaths. This gets rid of one of the many IP types in our codebase. Performance is currently worse across the board. This is likely due in part to netaddr.IP being a larger value type (4b -> 24b for IPv4, 16b -> 24b for IPv6), and in other part due to missing low-hanging fruit optimizations in netaddr. However, the regression is less bad than it looks at first glance, because we'd micro-optimized packet.IP* in the past few weeks. This change drops us back to roughly where we were at the 1.2 release, but with the benefit of a significant code and architectural simplification. name old time/op new time/op delta pkg:tailscale.com/net/packet goos:linux goarch:amd64 Decode/tcp4-8 12.2ns ± 5% 29.7ns ± 2% +142.32% (p=0.008 n=5+5) Decode/tcp6-8 12.6ns ± 3% 65.1ns ± 2% +418.47% (p=0.008 n=5+5) Decode/udp4-8 11.8ns ± 3% 30.5ns ± 2% +157.94% (p=0.008 n=5+5) Decode/udp6-8 27.1ns ± 1% 65.7ns ± 2% +142.36% (p=0.016 n=4+5) Decode/icmp4-8 24.6ns ± 2% 30.5ns ± 2% +23.65% (p=0.016 n=4+5) Decode/icmp6-8 22.9ns ±51% 65.5ns ± 2% +186.19% (p=0.008 n=5+5) Decode/igmp-8 18.1ns ±44% 30.2ns ± 1% +66.89% (p=0.008 n=5+5) Decode/unknown-8 20.8ns ± 1% 10.6ns ± 9% -49.11% (p=0.016 n=4+5) pkg:tailscale.com/wgengine/filter goos:linux goarch:amd64 Filter/icmp4-8 30.5ns ± 1% 77.9ns ± 3% +155.01% (p=0.008 n=5+5) Filter/tcp4_syn_in-8 43.7ns ± 3% 123.0ns ± 3% +181.72% (p=0.008 n=5+5) Filter/tcp4_syn_out-8 24.5ns ± 2% 45.7ns ± 6% +86.22% (p=0.008 n=5+5) Filter/udp4_in-8 64.8ns ± 1% 210.0ns ± 2% +223.87% (p=0.008 n=5+5) Filter/udp4_out-8 119ns ± 0% 278ns ± 0% +133.78% (p=0.016 n=4+5) Filter/icmp6-8 40.3ns ± 2% 204.4ns ± 4% +407.70% (p=0.008 n=5+5) Filter/tcp6_syn_in-8 35.3ns ± 3% 199.2ns ± 2% +464.95% (p=0.008 n=5+5) Filter/tcp6_syn_out-8 32.8ns ± 2% 81.0ns ± 2% +147.10% (p=0.008 n=5+5) Filter/udp6_in-8 106ns ± 2% 290ns ± 2% +174.48% (p=0.008 n=5+5) Filter/udp6_out-8 184ns ± 2% 314ns ± 3% +70.43% (p=0.016 n=4+5) pkg:tailscale.com/wgengine/tstun goos:linux goarch:amd64 Write-8 9.02ns ± 3% 8.92ns ± 1% ~ (p=0.421 n=5+5) name old alloc/op new alloc/op delta pkg:tailscale.com/net/packet goos:linux goarch:amd64 Decode/tcp4-8 0.00B 0.00B ~ (all equal) Decode/tcp6-8 0.00B 0.00B ~ (all equal) Decode/udp4-8 0.00B 0.00B ~ (all equal) Decode/udp6-8 0.00B 0.00B ~ (all equal) Decode/icmp4-8 0.00B 0.00B ~ (all equal) Decode/icmp6-8 0.00B 0.00B ~ (all equal) Decode/igmp-8 0.00B 0.00B ~ (all equal) Decode/unknown-8 0.00B 0.00B ~ (all equal) pkg:tailscale.com/wgengine/filter goos:linux goarch:amd64 Filter/icmp4-8 0.00B 0.00B ~ (all equal) Filter/tcp4_syn_in-8 0.00B 0.00B ~ (all equal) Filter/tcp4_syn_out-8 0.00B 0.00B ~ (all equal) Filter/udp4_in-8 0.00B 0.00B ~ (all equal) Filter/udp4_out-8 16.0B ± 0% 64.0B ± 0% +300.00% (p=0.008 n=5+5) Filter/icmp6-8 0.00B 0.00B ~ (all equal) Filter/tcp6_syn_in-8 0.00B 0.00B ~ (all equal) Filter/tcp6_syn_out-8 0.00B 0.00B ~ (all equal) Filter/udp6_in-8 0.00B 0.00B ~ (all equal) Filter/udp6_out-8 48.0B ± 0% 64.0B ± 0% +33.33% (p=0.008 n=5+5) name old allocs/op new allocs/op delta pkg:tailscale.com/net/packet goos:linux goarch:amd64 Decode/tcp4-8 0.00 0.00 ~ (all equal) Decode/tcp6-8 0.00 0.00 ~ (all equal) Decode/udp4-8 0.00 0.00 ~ (all equal) Decode/udp6-8 0.00 0.00 ~ (all equal) Decode/icmp4-8 0.00 0.00 ~ (all equal) Decode/icmp6-8 0.00 0.00 ~ (all equal) Decode/igmp-8 0.00 0.00 ~ (all equal) Decode/unknown-8 0.00 0.00 ~ (all equal) pkg:tailscale.com/wgengine/filter goos:linux goarch:amd64 Filter/icmp4-8 0.00 0.00 ~ (all equal) Filter/tcp4_syn_in-8 0.00 0.00 ~ (all equal) Filter/tcp4_syn_out-8 0.00 0.00 ~ (all equal) Filter/udp4_in-8 0.00 0.00 ~ (all equal) Filter/udp4_out-8 1.00 ± 0% 1.00 ± 0% ~ (all equal) Filter/icmp6-8 0.00 0.00 ~ (all equal) Filter/tcp6_syn_in-8 0.00 0.00 ~ (all equal) Filter/tcp6_syn_out-8 0.00 0.00 ~ (all equal) Filter/udp6_in-8 0.00 0.00 ~ (all equal) Filter/udp6_out-8 1.00 ± 0% 1.00 ± 0% ~ (all equal) Signed-off-by: David Anderson <danderson@tailscale.com>
447 lines
12 KiB
Go
447 lines
12 KiB
Go
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package packet
|
|
|
|
import (
|
|
"bytes"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"inet.af/netaddr"
|
|
)
|
|
|
|
func mustIPPort(s string) netaddr.IPPort {
|
|
ipp, err := netaddr.ParseIPPort(s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return ipp
|
|
}
|
|
|
|
var icmp4RequestBuffer = []byte{
|
|
// IP header up to checksum
|
|
0x45, 0x00, 0x00, 0x27, 0xde, 0xad, 0x00, 0x00, 0x40, 0x01, 0x8c, 0x15,
|
|
// source ip
|
|
0x01, 0x02, 0x03, 0x04,
|
|
// destination ip
|
|
0x05, 0x06, 0x07, 0x08,
|
|
// ICMP header
|
|
0x08, 0x00, 0x7d, 0x22,
|
|
// "request_payload"
|
|
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
|
|
}
|
|
|
|
var icmp4RequestDecode = Parsed{
|
|
b: icmp4RequestBuffer,
|
|
subofs: 20,
|
|
dataofs: 24,
|
|
length: len(icmp4RequestBuffer),
|
|
|
|
IPVersion: 4,
|
|
IPProto: ICMPv4,
|
|
Src: mustIPPort("1.2.3.4:0"),
|
|
Dst: mustIPPort("5.6.7.8:0"),
|
|
}
|
|
|
|
var icmp4ReplyBuffer = []byte{
|
|
0x45, 0x00, 0x00, 0x25, 0x21, 0x52, 0x00, 0x00, 0x40, 0x01, 0x49, 0x73,
|
|
// source ip
|
|
0x05, 0x06, 0x07, 0x08,
|
|
// destination ip
|
|
0x01, 0x02, 0x03, 0x04,
|
|
// ICMP header
|
|
0x00, 0x00, 0xe6, 0x9e,
|
|
// "reply_payload"
|
|
0x72, 0x65, 0x70, 0x6c, 0x79, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
|
|
}
|
|
|
|
var icmp4ReplyDecode = Parsed{
|
|
b: icmp4ReplyBuffer,
|
|
subofs: 20,
|
|
dataofs: 24,
|
|
length: len(icmp4ReplyBuffer),
|
|
|
|
IPVersion: 4,
|
|
IPProto: ICMPv4,
|
|
Src: mustIPPort("1.2.3.4:0"),
|
|
Dst: mustIPPort("5.6.7.8:0"),
|
|
}
|
|
|
|
// ICMPv6 Router Solicitation
|
|
var icmp6PacketBuffer = []byte{
|
|
0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a, 0xff,
|
|
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0xfb, 0x57, 0x1d, 0xea, 0x9c, 0x39, 0x8f, 0xb7,
|
|
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
|
|
0x85, 0x00, 0x38, 0x04, 0x00, 0x00, 0x00, 0x00,
|
|
}
|
|
|
|
var icmp6PacketDecode = Parsed{
|
|
b: icmp6PacketBuffer,
|
|
subofs: 40,
|
|
dataofs: 44,
|
|
length: len(icmp6PacketBuffer),
|
|
IPVersion: 6,
|
|
IPProto: ICMPv6,
|
|
Src: mustIPPort("[fe80::fb57:1dea:9c39:8fb7]:0"),
|
|
Dst: mustIPPort("[ff02::2]:0"),
|
|
}
|
|
|
|
// This is a malformed IPv4 packet.
|
|
// Namely, the string "tcp_payload" follows the first byte of the IPv4 header.
|
|
var unknownPacketBuffer = []byte{
|
|
0x45, 0x74, 0x63, 0x70, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
|
|
}
|
|
|
|
var unknownPacketDecode = Parsed{
|
|
b: unknownPacketBuffer,
|
|
IPVersion: 0,
|
|
IPProto: Unknown,
|
|
}
|
|
|
|
var tcp4PacketBuffer = []byte{
|
|
// IP header up to checksum
|
|
0x45, 0x00, 0x00, 0x37, 0xde, 0xad, 0x00, 0x00, 0x40, 0x06, 0x49, 0x5f,
|
|
// source ip
|
|
0x01, 0x02, 0x03, 0x04,
|
|
// destination ip
|
|
0x05, 0x06, 0x07, 0x08,
|
|
// TCP header with SYN, ACK set
|
|
0x00, 0x7b, 0x02, 0x37, 0x00, 0x00, 0x12, 0x34, 0x00, 0x00, 0x00, 0x00,
|
|
0x50, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// "request_payload"
|
|
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
|
|
}
|
|
|
|
var tcp4PacketDecode = Parsed{
|
|
b: tcp4PacketBuffer,
|
|
subofs: 20,
|
|
dataofs: 40,
|
|
length: len(tcp4PacketBuffer),
|
|
|
|
IPVersion: 4,
|
|
IPProto: TCP,
|
|
Src: mustIPPort("1.2.3.4:123"),
|
|
Dst: mustIPPort("5.6.7.8:567"),
|
|
TCPFlags: TCPSynAck,
|
|
}
|
|
|
|
var tcp6RequestBuffer = []byte{
|
|
// IPv6 header up to hop limit
|
|
0x60, 0x06, 0xef, 0xcc, 0x00, 0x28, 0x06, 0x40,
|
|
// Src addr
|
|
0x20, 0x01, 0x05, 0x59, 0xbc, 0x13, 0x54, 0x00, 0x17, 0x49, 0x46, 0x28, 0x39, 0x34, 0x0e, 0x1b,
|
|
// Dst addr
|
|
0x26, 0x07, 0xf8, 0xb0, 0x40, 0x0a, 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0e,
|
|
// TCP SYN segment, no payload
|
|
0xa4, 0x60, 0x00, 0x50, 0xf3, 0x82, 0xa1, 0x25, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, 0xfd, 0x20,
|
|
0xb1, 0xc6, 0x00, 0x00, 0x02, 0x04, 0x05, 0xa0, 0x04, 0x02, 0x08, 0x0a, 0xca, 0x76, 0xa6, 0x8e,
|
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x07,
|
|
}
|
|
|
|
var tcp6RequestDecode = Parsed{
|
|
b: tcp6RequestBuffer,
|
|
subofs: 40,
|
|
dataofs: len(tcp6RequestBuffer),
|
|
length: len(tcp6RequestBuffer),
|
|
|
|
IPVersion: 6,
|
|
IPProto: TCP,
|
|
Src: mustIPPort("[2001:559:bc13:5400:1749:4628:3934:e1b]:42080"),
|
|
Dst: mustIPPort("[2607:f8b0:400a:809::200e]:80"),
|
|
TCPFlags: TCPSyn,
|
|
}
|
|
|
|
var udp4RequestBuffer = []byte{
|
|
// IP header up to checksum
|
|
0x45, 0x00, 0x00, 0x2b, 0xde, 0xad, 0x00, 0x00, 0x40, 0x11, 0x8c, 0x01,
|
|
// source ip
|
|
0x01, 0x02, 0x03, 0x04,
|
|
// destination ip
|
|
0x05, 0x06, 0x07, 0x08,
|
|
// UDP header
|
|
0x00, 0x7b, 0x02, 0x37, 0x00, 0x17, 0x72, 0x1d,
|
|
// "request_payload"
|
|
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
|
|
}
|
|
|
|
var udp4RequestDecode = Parsed{
|
|
b: udp4RequestBuffer,
|
|
subofs: 20,
|
|
dataofs: 28,
|
|
length: len(udp4RequestBuffer),
|
|
|
|
IPVersion: 4,
|
|
IPProto: UDP,
|
|
Src: mustIPPort("1.2.3.4:123"),
|
|
Dst: mustIPPort("5.6.7.8:567"),
|
|
}
|
|
|
|
var invalid4RequestBuffer = []byte{
|
|
// IP header up to checksum. IHL field points beyond end of packet.
|
|
0x4a, 0x00, 0x00, 0x14, 0xde, 0xad, 0x00, 0x00, 0x40, 0x11, 0x8c, 0x01,
|
|
// source ip
|
|
0x01, 0x02, 0x03, 0x04,
|
|
// destination ip
|
|
0x05, 0x06, 0x07, 0x08,
|
|
}
|
|
|
|
// Regression check for the IHL field pointing beyond the end of the
|
|
// packet.
|
|
var invalid4RequestDecode = Parsed{
|
|
b: invalid4RequestBuffer,
|
|
subofs: 40,
|
|
length: len(invalid4RequestBuffer),
|
|
|
|
IPVersion: 4,
|
|
IPProto: Unknown,
|
|
Src: mustIPPort("1.2.3.4:0"),
|
|
Dst: mustIPPort("5.6.7.8:0"),
|
|
}
|
|
|
|
var udp6RequestBuffer = []byte{
|
|
// IPv6 header up to hop limit
|
|
0x60, 0x0e, 0xc9, 0x67, 0x00, 0x29, 0x11, 0x40,
|
|
// Src addr
|
|
0x20, 0x01, 0x05, 0x59, 0xbc, 0x13, 0x54, 0x00, 0x17, 0x49, 0x46, 0x28, 0x39, 0x34, 0x0e, 0x1b,
|
|
// Dst addr
|
|
0x26, 0x07, 0xf8, 0xb0, 0x40, 0x0a, 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0e,
|
|
// UDP header
|
|
0xd4, 0x04, 0x01, 0xbb, 0x00, 0x29, 0x96, 0x84,
|
|
// Payload
|
|
0x5c, 0x06, 0xae, 0x85, 0x02, 0xf5, 0xdb, 0x90, 0xe0, 0xe0, 0x93, 0xed, 0x9a, 0xd9, 0x92, 0x69, 0xbe, 0x36, 0x8a, 0x7d, 0xd7, 0xce, 0xd0, 0x8a, 0xf2, 0x51, 0x95, 0xff, 0xb6, 0x92, 0x70, 0x10, 0xd7,
|
|
}
|
|
|
|
var udp6RequestDecode = Parsed{
|
|
b: udp6RequestBuffer,
|
|
subofs: 40,
|
|
dataofs: 48,
|
|
length: len(udp6RequestBuffer),
|
|
|
|
IPVersion: 6,
|
|
IPProto: UDP,
|
|
Src: mustIPPort("[2001:559:bc13:5400:1749:4628:3934:e1b]:54276"),
|
|
Dst: mustIPPort("[2607:f8b0:400a:809::200e]:443"),
|
|
}
|
|
|
|
var udp4ReplyBuffer = []byte{
|
|
// IP header up to checksum
|
|
0x45, 0x00, 0x00, 0x29, 0x21, 0x52, 0x00, 0x00, 0x40, 0x11, 0x49, 0x5f,
|
|
// source ip
|
|
0x05, 0x06, 0x07, 0x08,
|
|
// destination ip
|
|
0x01, 0x02, 0x03, 0x04,
|
|
// UDP header
|
|
0x02, 0x37, 0x00, 0x7b, 0x00, 0x15, 0xd3, 0x9d,
|
|
// "reply_payload"
|
|
0x72, 0x65, 0x70, 0x6c, 0x79, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
|
|
}
|
|
|
|
var udp4ReplyDecode = Parsed{
|
|
b: udp4ReplyBuffer,
|
|
subofs: 20,
|
|
dataofs: 28,
|
|
length: len(udp4ReplyBuffer),
|
|
|
|
IPProto: UDP,
|
|
Src: mustIPPort("1.2.3.4:567"),
|
|
Dst: mustIPPort("5.6.7.8:123"),
|
|
}
|
|
|
|
var igmpPacketBuffer = []byte{
|
|
// IP header up to checksum
|
|
0x46, 0xc0, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x01, 0x02, 0x41, 0x22,
|
|
// source IP
|
|
0xc0, 0xa8, 0x01, 0x52,
|
|
// destination IP
|
|
0xe0, 0x00, 0x00, 0xfb,
|
|
// IGMP Membership Report
|
|
0x94, 0x04, 0x00, 0x00, 0x16, 0x00, 0x09, 0x04, 0xe0, 0x00, 0x00, 0xfb,
|
|
//0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
}
|
|
|
|
var igmpPacketDecode = Parsed{
|
|
b: igmpPacketBuffer,
|
|
subofs: 24,
|
|
length: len(igmpPacketBuffer),
|
|
|
|
IPVersion: 4,
|
|
IPProto: IGMP,
|
|
Src: mustIPPort("192.168.1.82:0"),
|
|
Dst: mustIPPort("224.0.0.251:0"),
|
|
}
|
|
|
|
func TestParsed(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
qdecode Parsed
|
|
want string
|
|
}{
|
|
{"tcp4", tcp4PacketDecode, "TCP{1.2.3.4:123 > 5.6.7.8:567}"},
|
|
{"tcp6", tcp6RequestDecode, "TCP{[2001:559:bc13:5400:1749:4628:3934:e1b]:42080 > [2607:f8b0:400a:809::200e]:80}"},
|
|
{"udp4", udp4RequestDecode, "UDP{1.2.3.4:123 > 5.6.7.8:567}"},
|
|
{"udp6", udp6RequestDecode, "UDP{[2001:559:bc13:5400:1749:4628:3934:e1b]:54276 > [2607:f8b0:400a:809::200e]:443}"},
|
|
{"icmp4", icmp4RequestDecode, "ICMPv4{1.2.3.4:0 > 5.6.7.8:0}"},
|
|
{"icmp6", icmp6PacketDecode, "ICMPv6{[fe80::fb57:1dea:9c39:8fb7]:0 > [ff02::2]:0}"},
|
|
{"igmp", igmpPacketDecode, "IGMP{192.168.1.82:0 > 224.0.0.251:0}"},
|
|
{"unknown", unknownPacketDecode, "Unknown{???}"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := tt.qdecode.String()
|
|
if got != tt.want {
|
|
t.Errorf("got %q; want %q", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
|
|
var sink string
|
|
allocs := testing.AllocsPerRun(1000, func() {
|
|
sink = tests[0].qdecode.String()
|
|
})
|
|
_ = sink
|
|
if allocs != 1 {
|
|
t.Errorf("allocs = %v; want 1", allocs)
|
|
}
|
|
}
|
|
|
|
func TestDecode(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
buf []byte
|
|
want Parsed
|
|
}{
|
|
{"icmp4", icmp4RequestBuffer, icmp4RequestDecode},
|
|
{"icmp6", icmp6PacketBuffer, icmp6PacketDecode},
|
|
{"tcp4", tcp4PacketBuffer, tcp4PacketDecode},
|
|
{"tcp6", tcp6RequestBuffer, tcp6RequestDecode},
|
|
{"udp4", udp4RequestBuffer, udp4RequestDecode},
|
|
{"udp6", udp6RequestBuffer, udp6RequestDecode},
|
|
{"igmp", igmpPacketBuffer, igmpPacketDecode},
|
|
{"unknown", unknownPacketBuffer, unknownPacketDecode},
|
|
{"invalid4", invalid4RequestBuffer, invalid4RequestDecode},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
var got Parsed
|
|
got.Decode(tt.buf)
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("mismatch\n got: %#v\nwant: %#v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
|
|
allocs := testing.AllocsPerRun(1000, func() {
|
|
var got Parsed
|
|
got.Decode(tests[0].buf)
|
|
})
|
|
if allocs != 0 {
|
|
t.Errorf("allocs = %v; want 0", allocs)
|
|
}
|
|
}
|
|
|
|
func BenchmarkDecode(b *testing.B) {
|
|
benches := []struct {
|
|
name string
|
|
buf []byte
|
|
}{
|
|
{"tcp4", tcp4PacketBuffer},
|
|
{"tcp6", tcp6RequestBuffer},
|
|
{"udp4", udp4RequestBuffer},
|
|
{"udp6", udp6RequestBuffer},
|
|
{"icmp4", icmp4RequestBuffer},
|
|
{"icmp6", icmp6PacketBuffer},
|
|
{"igmp", igmpPacketBuffer},
|
|
{"unknown", unknownPacketBuffer},
|
|
}
|
|
|
|
for _, bench := range benches {
|
|
b.Run(bench.name, func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
var p Parsed
|
|
p.Decode(bench.buf)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMarshalRequest(t *testing.T) {
|
|
// Too small to hold our packets, but only barely.
|
|
var small [20]byte
|
|
var large [64]byte
|
|
|
|
icmpHeader := icmp4RequestDecode.ICMP4Header()
|
|
udpHeader := udp4RequestDecode.UDP4Header()
|
|
tests := []struct {
|
|
name string
|
|
header Header
|
|
want []byte
|
|
}{
|
|
{"icmp", &icmpHeader, icmp4RequestBuffer},
|
|
{"udp", &udpHeader, udp4RequestBuffer},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := tt.header.Marshal(small[:])
|
|
if err != errSmallBuffer {
|
|
t.Errorf("got err: nil; want: %s", errSmallBuffer)
|
|
}
|
|
|
|
dataOffset := tt.header.Len()
|
|
dataLength := copy(large[dataOffset:], []byte("request_payload"))
|
|
end := dataOffset + dataLength
|
|
err = tt.header.Marshal(large[:end])
|
|
|
|
if err != nil {
|
|
t.Errorf("got err: %s; want nil", err)
|
|
}
|
|
|
|
if !bytes.Equal(large[:end], tt.want) {
|
|
t.Errorf("got %x; want %x", large[:end], tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMarshalResponse(t *testing.T) {
|
|
var buf [64]byte
|
|
|
|
icmpHeader := icmp4RequestDecode.ICMP4Header()
|
|
udpHeader := udp4RequestDecode.UDP4Header()
|
|
|
|
tests := []struct {
|
|
name string
|
|
header Header
|
|
want []byte
|
|
}{
|
|
{"icmp", &icmpHeader, icmp4ReplyBuffer},
|
|
{"udp", &udpHeader, udp4ReplyBuffer},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tt.header.ToResponse()
|
|
|
|
dataOffset := tt.header.Len()
|
|
dataLength := copy(buf[dataOffset:], []byte("reply_payload"))
|
|
end := dataOffset + dataLength
|
|
err := tt.header.Marshal(buf[:end])
|
|
|
|
if err != nil {
|
|
t.Errorf("got err: %s; want nil", err)
|
|
}
|
|
|
|
if !bytes.Equal(buf[:end], tt.want) {
|
|
t.Errorf("got %x; want %x", buf[:end], tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|