mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 13:05:46 +00:00
net/packet: s/ParsedPacket/Parsed/ to avoid package stuttering.
Signed-off-by: David Anderson <danderson@tailscale.com>
This commit is contained in:
parent
c48253e63b
commit
093431f5dd
@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
// Package packet contains packet parsing and marshaling utilities.
|
// Package packet contains packet parsing and marshaling utilities.
|
||||||
//
|
//
|
||||||
// ParsedPacket provides allocation-free minimal packet header
|
// Parsed provides allocation-free minimal packet header decoding, for
|
||||||
// decoding, for use in packet filtering. The other types in the
|
// use in packet filtering. The other types in the package are for
|
||||||
// package are for constructing and marshaling packets into []bytes.
|
// constructing and marshaling packets into []bytes.
|
||||||
//
|
//
|
||||||
// To support allocation-free parsing, this package defines IPv4 and
|
// To support allocation-free parsing, this package defines IPv4 and
|
||||||
// IPv6 address types. You should prefer to use netaddr's types,
|
// IPv6 address types. You should prefer to use netaddr's types,
|
||||||
|
@ -29,10 +29,10 @@
|
|||||||
put32 = binary.BigEndian.PutUint32
|
put32 = binary.BigEndian.PutUint32
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParsedPacket is a minimal decoding of a packet suitable for use in filters.
|
// Parsed is a minimal decoding of a packet suitable for use in filters.
|
||||||
//
|
//
|
||||||
// In general, it only supports IPv4. The IPv6 parsing is very minimal.
|
// In general, it only supports IPv4. The IPv6 parsing is very minimal.
|
||||||
type ParsedPacket struct {
|
type Parsed struct {
|
||||||
// b is the byte buffer that this decodes.
|
// b is the byte buffer that this decodes.
|
||||||
b []byte
|
b []byte
|
||||||
// subofs is the offset of IP subprotocol.
|
// subofs is the offset of IP subprotocol.
|
||||||
@ -55,7 +55,7 @@ type ParsedPacket struct {
|
|||||||
// NextHeader
|
// NextHeader
|
||||||
type NextHeader uint8
|
type NextHeader uint8
|
||||||
|
|
||||||
func (p *ParsedPacket) String() string {
|
func (p *Parsed) String() string {
|
||||||
if p.IPVersion == 6 {
|
if p.IPVersion == 6 {
|
||||||
return fmt.Sprintf("IPv6{Proto=%d}", p.IPProto)
|
return fmt.Sprintf("IPv6{Proto=%d}", p.IPProto)
|
||||||
}
|
}
|
||||||
@ -108,7 +108,7 @@ func ipChecksum(b []byte) uint16 {
|
|||||||
// It performs extremely simple packet decoding for basic IPv4 packet types.
|
// It performs extremely simple packet decoding for basic IPv4 packet types.
|
||||||
// It extracts only the subprotocol id, IP addresses, and (if any) ports,
|
// It extracts only the subprotocol id, IP addresses, and (if any) ports,
|
||||||
// and shouldn't need any memory allocation.
|
// and shouldn't need any memory allocation.
|
||||||
func (q *ParsedPacket) Decode(b []byte) {
|
func (q *Parsed) Decode(b []byte) {
|
||||||
q.b = b
|
q.b = b
|
||||||
|
|
||||||
if len(b) < ipHeaderLength {
|
if len(b) < ipHeaderLength {
|
||||||
@ -224,7 +224,7 @@ func (q *ParsedPacket) Decode(b []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *ParsedPacket) IPHeader() IP4Header {
|
func (q *Parsed) IPHeader() IP4Header {
|
||||||
ipid := get16(q.b[4:6])
|
ipid := get16(q.b[4:6])
|
||||||
return IP4Header{
|
return IP4Header{
|
||||||
IPID: ipid,
|
IPID: ipid,
|
||||||
@ -234,7 +234,7 @@ func (q *ParsedPacket) IPHeader() IP4Header {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *ParsedPacket) ICMPHeader() ICMP4Header {
|
func (q *Parsed) ICMPHeader() ICMP4Header {
|
||||||
return ICMP4Header{
|
return ICMP4Header{
|
||||||
IP4Header: q.IPHeader(),
|
IP4Header: q.IPHeader(),
|
||||||
Type: ICMP4Type(q.b[q.subofs+0]),
|
Type: ICMP4Type(q.b[q.subofs+0]),
|
||||||
@ -242,7 +242,7 @@ func (q *ParsedPacket) ICMPHeader() ICMP4Header {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *ParsedPacket) UDPHeader() UDP4Header {
|
func (q *Parsed) UDPHeader() UDP4Header {
|
||||||
return UDP4Header{
|
return UDP4Header{
|
||||||
IP4Header: q.IPHeader(),
|
IP4Header: q.IPHeader(),
|
||||||
SrcPort: q.SrcPort,
|
SrcPort: q.SrcPort,
|
||||||
@ -252,37 +252,37 @@ func (q *ParsedPacket) UDPHeader() UDP4Header {
|
|||||||
|
|
||||||
// Buffer returns the entire packet buffer.
|
// Buffer returns the entire packet buffer.
|
||||||
// This is a read-only view; that is, q retains the ownership of the buffer.
|
// This is a read-only view; that is, q retains the ownership of the buffer.
|
||||||
func (q *ParsedPacket) Buffer() []byte {
|
func (q *Parsed) Buffer() []byte {
|
||||||
return q.b
|
return q.b
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sub returns the IP subprotocol section.
|
// Sub returns the IP subprotocol section.
|
||||||
// This is a read-only view; that is, q retains the ownership of the buffer.
|
// This is a read-only view; that is, q retains the ownership of the buffer.
|
||||||
func (q *ParsedPacket) Sub(begin, n int) []byte {
|
func (q *Parsed) Sub(begin, n int) []byte {
|
||||||
return q.b[q.subofs+begin : q.subofs+begin+n]
|
return q.b[q.subofs+begin : q.subofs+begin+n]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Payload returns the payload of the IP subprotocol section.
|
// Payload returns the payload of the IP subprotocol section.
|
||||||
// This is a read-only view; that is, q retains the ownership of the buffer.
|
// This is a read-only view; that is, q retains the ownership of the buffer.
|
||||||
func (q *ParsedPacket) Payload() []byte {
|
func (q *Parsed) Payload() []byte {
|
||||||
return q.b[q.dataofs:q.length]
|
return q.b[q.dataofs:q.length]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trim trims the buffer to its IPv4 length.
|
// Trim trims the buffer to its IPv4 length.
|
||||||
// Sometimes packets arrive from an interface with extra bytes on the end.
|
// Sometimes packets arrive from an interface with extra bytes on the end.
|
||||||
// This removes them.
|
// This removes them.
|
||||||
func (q *ParsedPacket) Trim() []byte {
|
func (q *Parsed) Trim() []byte {
|
||||||
return q.b[:q.length]
|
return q.b[:q.length]
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsTCPSyn reports whether q is a TCP SYN packet
|
// IsTCPSyn reports whether q is a TCP SYN packet
|
||||||
// (i.e. the first packet in a new connection).
|
// (i.e. the first packet in a new connection).
|
||||||
func (q *ParsedPacket) IsTCPSyn() bool {
|
func (q *Parsed) IsTCPSyn() bool {
|
||||||
return (q.TCPFlags & TCPSynAck) == TCPSyn
|
return (q.TCPFlags & TCPSynAck) == TCPSyn
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsError reports whether q is an IPv4 ICMP "Error" packet.
|
// IsError reports whether q is an IPv4 ICMP "Error" packet.
|
||||||
func (q *ParsedPacket) IsError() bool {
|
func (q *Parsed) IsError() bool {
|
||||||
if q.IPProto == ICMP && len(q.b) >= q.subofs+8 {
|
if q.IPProto == ICMP && len(q.b) >= q.subofs+8 {
|
||||||
switch ICMP4Type(q.b[q.subofs]) {
|
switch ICMP4Type(q.b[q.subofs]) {
|
||||||
case ICMP4Unreachable, ICMP4TimeExceeded:
|
case ICMP4Unreachable, ICMP4TimeExceeded:
|
||||||
@ -293,7 +293,7 @@ func (q *ParsedPacket) IsError() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IsEchoRequest reports whether q is an IPv4 ICMP Echo Request.
|
// IsEchoRequest reports whether q is an IPv4 ICMP Echo Request.
|
||||||
func (q *ParsedPacket) IsEchoRequest() bool {
|
func (q *Parsed) IsEchoRequest() bool {
|
||||||
if q.IPProto == ICMP && len(q.b) >= q.subofs+8 {
|
if q.IPProto == ICMP && len(q.b) >= q.subofs+8 {
|
||||||
return ICMP4Type(q.b[q.subofs]) == ICMP4EchoRequest &&
|
return ICMP4Type(q.b[q.subofs]) == ICMP4EchoRequest &&
|
||||||
ICMP4Code(q.b[q.subofs+1]) == ICMP4NoCode
|
ICMP4Code(q.b[q.subofs+1]) == ICMP4NoCode
|
||||||
@ -302,7 +302,7 @@ func (q *ParsedPacket) IsEchoRequest() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IsEchoRequest reports whether q is an IPv4 ICMP Echo Response.
|
// IsEchoRequest reports whether q is an IPv4 ICMP Echo Response.
|
||||||
func (q *ParsedPacket) IsEchoResponse() bool {
|
func (q *Parsed) IsEchoResponse() bool {
|
||||||
if q.IPProto == ICMP && len(q.b) >= q.subofs+8 {
|
if q.IPProto == ICMP && len(q.b) >= q.subofs+8 {
|
||||||
return ICMP4Type(q.b[q.subofs]) == ICMP4EchoReply &&
|
return ICMP4Type(q.b[q.subofs]) == ICMP4EchoReply &&
|
||||||
ICMP4Code(q.b[q.subofs+1]) == ICMP4NoCode
|
ICMP4Code(q.b[q.subofs+1]) == ICMP4NoCode
|
||||||
|
@ -41,7 +41,7 @@ func TestIP4String(t *testing.T) {
|
|||||||
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
|
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
|
||||||
}
|
}
|
||||||
|
|
||||||
var icmpRequestDecode = ParsedPacket{
|
var icmpRequestDecode = Parsed{
|
||||||
b: icmpRequestBuffer,
|
b: icmpRequestBuffer,
|
||||||
subofs: 20,
|
subofs: 20,
|
||||||
dataofs: 24,
|
dataofs: 24,
|
||||||
@ -67,7 +67,7 @@ func TestIP4String(t *testing.T) {
|
|||||||
0x72, 0x65, 0x70, 0x6c, 0x79, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
|
0x72, 0x65, 0x70, 0x6c, 0x79, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
|
||||||
}
|
}
|
||||||
|
|
||||||
var icmpReplyDecode = ParsedPacket{
|
var icmpReplyDecode = Parsed{
|
||||||
b: icmpReplyBuffer,
|
b: icmpReplyBuffer,
|
||||||
subofs: 20,
|
subofs: 20,
|
||||||
dataofs: 24,
|
dataofs: 24,
|
||||||
@ -91,7 +91,7 @@ func TestIP4String(t *testing.T) {
|
|||||||
0x85, 0x00, 0x38, 0x04, 0x00, 0x00, 0x00, 0x00,
|
0x85, 0x00, 0x38, 0x04, 0x00, 0x00, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ipv6PacketDecode = ParsedPacket{
|
var ipv6PacketDecode = Parsed{
|
||||||
b: ipv6PacketBuffer,
|
b: ipv6PacketBuffer,
|
||||||
IPVersion: 6,
|
IPVersion: 6,
|
||||||
IPProto: ICMPv6,
|
IPProto: ICMPv6,
|
||||||
@ -103,7 +103,7 @@ func TestIP4String(t *testing.T) {
|
|||||||
0x45, 0x74, 0x63, 0x70, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
|
0x45, 0x74, 0x63, 0x70, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
|
||||||
}
|
}
|
||||||
|
|
||||||
var unknownPacketDecode = ParsedPacket{
|
var unknownPacketDecode = Parsed{
|
||||||
b: unknownPacketBuffer,
|
b: unknownPacketBuffer,
|
||||||
IPVersion: 0,
|
IPVersion: 0,
|
||||||
IPProto: Unknown,
|
IPProto: Unknown,
|
||||||
@ -123,7 +123,7 @@ func TestIP4String(t *testing.T) {
|
|||||||
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
|
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
|
||||||
}
|
}
|
||||||
|
|
||||||
var tcpPacketDecode = ParsedPacket{
|
var tcpPacketDecode = Parsed{
|
||||||
b: tcpPacketBuffer,
|
b: tcpPacketBuffer,
|
||||||
subofs: 20,
|
subofs: 20,
|
||||||
dataofs: 40,
|
dataofs: 40,
|
||||||
@ -151,7 +151,7 @@ func TestIP4String(t *testing.T) {
|
|||||||
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
|
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
|
||||||
}
|
}
|
||||||
|
|
||||||
var udpRequestDecode = ParsedPacket{
|
var udpRequestDecode = Parsed{
|
||||||
b: udpRequestBuffer,
|
b: udpRequestBuffer,
|
||||||
subofs: 20,
|
subofs: 20,
|
||||||
dataofs: 28,
|
dataofs: 28,
|
||||||
@ -178,7 +178,7 @@ func TestIP4String(t *testing.T) {
|
|||||||
0x72, 0x65, 0x70, 0x6c, 0x79, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
|
0x72, 0x65, 0x70, 0x6c, 0x79, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
|
||||||
}
|
}
|
||||||
|
|
||||||
var udpReplyDecode = ParsedPacket{
|
var udpReplyDecode = Parsed{
|
||||||
b: udpReplyBuffer,
|
b: udpReplyBuffer,
|
||||||
subofs: 20,
|
subofs: 20,
|
||||||
dataofs: 28,
|
dataofs: 28,
|
||||||
@ -191,10 +191,10 @@ func TestIP4String(t *testing.T) {
|
|||||||
DstPort: 123,
|
DstPort: 123,
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParsedPacket(t *testing.T) {
|
func TestParsed(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
qdecode ParsedPacket
|
qdecode Parsed
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{"tcp", tcpPacketDecode, "TCP{1.2.3.4:123 > 5.6.7.8:567}"},
|
{"tcp", tcpPacketDecode, "TCP{1.2.3.4:123 > 5.6.7.8:567}"},
|
||||||
@ -226,7 +226,7 @@ func TestDecode(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
buf []byte
|
buf []byte
|
||||||
want ParsedPacket
|
want Parsed
|
||||||
}{
|
}{
|
||||||
{"icmp", icmpRequestBuffer, icmpRequestDecode},
|
{"icmp", icmpRequestBuffer, icmpRequestDecode},
|
||||||
{"ipv6", ipv6PacketBuffer, ipv6PacketDecode},
|
{"ipv6", ipv6PacketBuffer, ipv6PacketDecode},
|
||||||
@ -237,7 +237,7 @@ func TestDecode(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
var got ParsedPacket
|
var got Parsed
|
||||||
got.Decode(tt.buf)
|
got.Decode(tt.buf)
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
t.Errorf("mismatch\n got: %#v\nwant: %#v", got, tt.want)
|
t.Errorf("mismatch\n got: %#v\nwant: %#v", got, tt.want)
|
||||||
@ -246,7 +246,7 @@ func TestDecode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
allocs := testing.AllocsPerRun(1000, func() {
|
allocs := testing.AllocsPerRun(1000, func() {
|
||||||
var got ParsedPacket
|
var got Parsed
|
||||||
got.Decode(tests[0].buf)
|
got.Decode(tests[0].buf)
|
||||||
})
|
})
|
||||||
if allocs != 0 {
|
if allocs != 0 {
|
||||||
@ -267,7 +267,7 @@ func BenchmarkDecode(b *testing.B) {
|
|||||||
for _, bench := range benches {
|
for _, bench := range benches {
|
||||||
b.Run(bench.name, func(b *testing.B) {
|
b.Run(bench.name, func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
var p ParsedPacket
|
var p Parsed
|
||||||
p.Decode(bench.buf)
|
p.Decode(bench.buf)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -153,7 +153,7 @@ func maybeHexdump(flag RunFlags, b []byte) string {
|
|||||||
var acceptBucket = rate.NewLimiter(rate.Every(10*time.Second), 3)
|
var acceptBucket = rate.NewLimiter(rate.Every(10*time.Second), 3)
|
||||||
var dropBucket = rate.NewLimiter(rate.Every(5*time.Second), 10)
|
var dropBucket = rate.NewLimiter(rate.Every(5*time.Second), 10)
|
||||||
|
|
||||||
func (f *Filter) logRateLimit(runflags RunFlags, q *packet.ParsedPacket, dir direction, r Response, why string) {
|
func (f *Filter) logRateLimit(runflags RunFlags, q *packet.Parsed, dir direction, r Response, why string) {
|
||||||
var verdict string
|
var verdict string
|
||||||
|
|
||||||
if r == Drop && omitDropLogging(q, dir) {
|
if r == Drop && omitDropLogging(q, dir) {
|
||||||
@ -186,7 +186,7 @@ func (f *Filter) logRateLimit(runflags RunFlags, q *packet.ParsedPacket, dir dir
|
|||||||
// CheckTCP determines whether TCP traffic from srcIP to dstIP:dstPort
|
// CheckTCP determines whether TCP traffic from srcIP to dstIP:dstPort
|
||||||
// is allowed.
|
// is allowed.
|
||||||
func (f *Filter) CheckTCP(srcIP, dstIP netaddr.IP, dstPort uint16) Response {
|
func (f *Filter) CheckTCP(srcIP, dstIP netaddr.IP, dstPort uint16) Response {
|
||||||
pkt := &packet.ParsedPacket{}
|
pkt := &packet.Parsed{}
|
||||||
pkt.Decode(dummyPacket) // initialize private fields
|
pkt.Decode(dummyPacket) // initialize private fields
|
||||||
pkt.IPVersion = 4
|
pkt.IPVersion = 4
|
||||||
pkt.IPProto = packet.TCP
|
pkt.IPProto = packet.TCP
|
||||||
@ -201,7 +201,7 @@ func (f *Filter) CheckTCP(srcIP, dstIP netaddr.IP, dstPort uint16) Response {
|
|||||||
|
|
||||||
// RunIn determines whether this node is allowed to receive q from a
|
// RunIn determines whether this node is allowed to receive q from a
|
||||||
// Tailscale peer.
|
// Tailscale peer.
|
||||||
func (f *Filter) RunIn(q *packet.ParsedPacket, rf RunFlags) Response {
|
func (f *Filter) RunIn(q *packet.Parsed, rf RunFlags) Response {
|
||||||
dir := in
|
dir := in
|
||||||
r := f.pre(q, rf, dir)
|
r := f.pre(q, rf, dir)
|
||||||
if r == Accept || r == Drop {
|
if r == Accept || r == Drop {
|
||||||
@ -216,7 +216,7 @@ func (f *Filter) RunIn(q *packet.ParsedPacket, rf RunFlags) Response {
|
|||||||
|
|
||||||
// RunOut determines whether this node is allowed to send q to a
|
// RunOut determines whether this node is allowed to send q to a
|
||||||
// Tailscale peer.
|
// Tailscale peer.
|
||||||
func (f *Filter) RunOut(q *packet.ParsedPacket, rf RunFlags) Response {
|
func (f *Filter) RunOut(q *packet.Parsed, rf RunFlags) Response {
|
||||||
dir := out
|
dir := out
|
||||||
r := f.pre(q, rf, dir)
|
r := f.pre(q, rf, dir)
|
||||||
if r == Drop || r == Accept {
|
if r == Drop || r == Accept {
|
||||||
@ -229,7 +229,7 @@ func (f *Filter) RunOut(q *packet.ParsedPacket, rf RunFlags) Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// runIn runs the input-specific part of the filter logic.
|
// runIn runs the input-specific part of the filter logic.
|
||||||
func (f *Filter) runIn(q *packet.ParsedPacket) (r Response, why string) {
|
func (f *Filter) runIn(q *packet.Parsed) (r Response, why string) {
|
||||||
// A compromised peer could try to send us packets for
|
// A compromised peer could try to send us packets for
|
||||||
// destinations we didn't explicitly advertise. This check is to
|
// destinations we didn't explicitly advertise. This check is to
|
||||||
// prevent that.
|
// prevent that.
|
||||||
@ -290,7 +290,7 @@ func (f *Filter) runIn(q *packet.ParsedPacket) (r Response, why string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// runIn runs the output-specific part of the filter logic.
|
// runIn runs the output-specific part of the filter logic.
|
||||||
func (f *Filter) runOut(q *packet.ParsedPacket) (r Response, why string) {
|
func (f *Filter) runOut(q *packet.Parsed) (r Response, why string) {
|
||||||
if q.IPProto == packet.UDP {
|
if q.IPProto == packet.UDP {
|
||||||
t := tuple{q.DstIP, q.SrcIP, q.DstPort, q.SrcPort}
|
t := tuple{q.DstIP, q.SrcIP, q.DstPort, q.SrcPort}
|
||||||
var ti interface{} = t // allocate once, rather than twice inside mutex
|
var ti interface{} = t // allocate once, rather than twice inside mutex
|
||||||
@ -324,7 +324,7 @@ func (d direction) String() string {
|
|||||||
|
|
||||||
// pre runs the direction-agnostic filter logic. dir is only used for
|
// pre runs the direction-agnostic filter logic. dir is only used for
|
||||||
// logging.
|
// logging.
|
||||||
func (f *Filter) pre(q *packet.ParsedPacket, rf RunFlags, dir direction) Response {
|
func (f *Filter) pre(q *packet.Parsed, rf RunFlags, dir direction) Response {
|
||||||
if len(q.Buffer()) == 0 {
|
if len(q.Buffer()) == 0 {
|
||||||
// wireguard keepalive packet, always permit.
|
// wireguard keepalive packet, always permit.
|
||||||
return Accept
|
return Accept
|
||||||
@ -354,7 +354,7 @@ func (f *Filter) pre(q *packet.ParsedPacket, rf RunFlags, dir direction) Respons
|
|||||||
return Drop
|
return Drop
|
||||||
case packet.Fragment:
|
case packet.Fragment:
|
||||||
// Fragments after the first always need to be passed through.
|
// Fragments after the first always need to be passed through.
|
||||||
// Very small fragments are considered Junk by ParsedPacket.
|
// Very small fragments are considered Junk by Parsed.
|
||||||
f.logRateLimit(rf, q, dir, Accept, "fragment")
|
f.logRateLimit(rf, q, dir, Accept, "fragment")
|
||||||
return Accept
|
return Accept
|
||||||
}
|
}
|
||||||
@ -373,13 +373,13 @@ func (f *Filter) pre(q *packet.ParsedPacket, rf RunFlags, dir direction) Respons
|
|||||||
// deemed a packet to Drop, should bypass the [rate-limited] logging.
|
// deemed a packet to Drop, should bypass the [rate-limited] logging.
|
||||||
// We don't want to log scary & spammy reject warnings for packets
|
// We don't want to log scary & spammy reject warnings for packets
|
||||||
// that are totally normal, like IPv6 route announcements.
|
// that are totally normal, like IPv6 route announcements.
|
||||||
func omitDropLogging(p *packet.ParsedPacket, dir direction) bool {
|
func omitDropLogging(p *packet.Parsed, dir direction) bool {
|
||||||
b := p.Buffer()
|
b := p.Buffer()
|
||||||
switch dir {
|
switch dir {
|
||||||
case out:
|
case out:
|
||||||
switch p.IPVersion {
|
switch p.IPVersion {
|
||||||
case 4:
|
case 4:
|
||||||
// ParsedPacket.Decode zeros out ParsedPacket.IPProtocol for protocols
|
// Parsed.Decode zeros out Parsed.IPProtocol for protocols
|
||||||
// it doesn't know about, so parse it out ourselves if needed.
|
// it doesn't know about, so parse it out ourselves if needed.
|
||||||
ipProto := p.IPProto
|
ipProto := p.IPProto
|
||||||
if ipProto == 0 && len(b) > 8 {
|
if ipProto == 0 && len(b) > 8 {
|
||||||
|
@ -134,7 +134,7 @@ func TestFilter(t *testing.T) {
|
|||||||
|
|
||||||
type InOut struct {
|
type InOut struct {
|
||||||
want Response
|
want Response
|
||||||
p packet.ParsedPacket
|
p packet.Parsed
|
||||||
}
|
}
|
||||||
tests := []InOut{
|
tests := []InOut{
|
||||||
// Basic
|
// Basic
|
||||||
@ -199,7 +199,7 @@ func TestNoAllocs(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
got := int(testing.AllocsPerRun(1000, func() {
|
got := int(testing.AllocsPerRun(1000, func() {
|
||||||
q := &packet.ParsedPacket{}
|
q := &packet.Parsed{}
|
||||||
q.Decode(test.packet)
|
q.Decode(test.packet)
|
||||||
if test.in {
|
if test.in {
|
||||||
acl.RunIn(q, 0)
|
acl.RunIn(q, 0)
|
||||||
@ -274,7 +274,7 @@ func BenchmarkFilter(b *testing.B) {
|
|||||||
b.Run(bench.name, func(b *testing.B) {
|
b.Run(bench.name, func(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
q := &packet.ParsedPacket{}
|
q := &packet.Parsed{}
|
||||||
q.Decode(bench.packet)
|
q.Decode(bench.packet)
|
||||||
// This branch seems to have no measurable impact on performance.
|
// This branch seems to have no measurable impact on performance.
|
||||||
if bench.in {
|
if bench.in {
|
||||||
@ -303,7 +303,7 @@ func TestPreFilter(t *testing.T) {
|
|||||||
}
|
}
|
||||||
f := NewAllowNone(t.Logf)
|
f := NewAllowNone(t.Logf)
|
||||||
for _, testPacket := range packets {
|
for _, testPacket := range packets {
|
||||||
p := &packet.ParsedPacket{}
|
p := &packet.Parsed{}
|
||||||
p.Decode(testPacket.b)
|
p.Decode(testPacket.b)
|
||||||
got := f.pre(p, LogDrops|LogAccepts, in)
|
got := f.pre(p, LogDrops|LogAccepts, in)
|
||||||
if got != testPacket.want {
|
if got != testPacket.want {
|
||||||
@ -312,8 +312,8 @@ func TestPreFilter(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsed(proto packet.IP4Proto, src, dst packet.IP4, sport, dport uint16) packet.ParsedPacket {
|
func parsed(proto packet.IP4Proto, src, dst packet.IP4, sport, dport uint16) packet.Parsed {
|
||||||
return packet.ParsedPacket{
|
return packet.Parsed{
|
||||||
IPProto: proto,
|
IPProto: proto,
|
||||||
SrcIP: src,
|
SrcIP: src,
|
||||||
DstIP: dst,
|
DstIP: dst,
|
||||||
@ -385,13 +385,13 @@ func rawdefault(proto packet.IP4Proto, trimLength int) []byte {
|
|||||||
return rawpacket(proto, ip, ip, port, port, trimLength)
|
return rawpacket(proto, ip, ip, port, port, trimLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseHexPkt(t *testing.T, h string) *packet.ParsedPacket {
|
func parseHexPkt(t *testing.T, h string) *packet.Parsed {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
b, err := hex.DecodeString(strings.ReplaceAll(h, " ", ""))
|
b, err := hex.DecodeString(strings.ReplaceAll(h, " ", ""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to read hex %q: %v", h, err)
|
t.Fatalf("failed to read hex %q: %v", h, err)
|
||||||
}
|
}
|
||||||
p := new(packet.ParsedPacket)
|
p := new(packet.Parsed)
|
||||||
p.Decode(b)
|
p.Decode(b)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
@ -399,13 +399,13 @@ func parseHexPkt(t *testing.T, h string) *packet.ParsedPacket {
|
|||||||
func TestOmitDropLogging(t *testing.T) {
|
func TestOmitDropLogging(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
pkt *packet.ParsedPacket
|
pkt *packet.Parsed
|
||||||
dir direction
|
dir direction
|
||||||
want bool
|
want bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "v4_tcp_out",
|
name: "v4_tcp_out",
|
||||||
pkt: &packet.ParsedPacket{IPVersion: 4, IPProto: packet.TCP},
|
pkt: &packet.Parsed{IPVersion: 4, IPProto: packet.TCP},
|
||||||
dir: out,
|
dir: out,
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
@ -435,19 +435,19 @@ func TestOmitDropLogging(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "v4_multicast_out_low",
|
name: "v4_multicast_out_low",
|
||||||
pkt: &packet.ParsedPacket{IPVersion: 4, DstIP: packet.NewIP4(net.ParseIP("224.0.0.0"))},
|
pkt: &packet.Parsed{IPVersion: 4, DstIP: packet.NewIP4(net.ParseIP("224.0.0.0"))},
|
||||||
dir: out,
|
dir: out,
|
||||||
want: true,
|
want: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "v4_multicast_out_high",
|
name: "v4_multicast_out_high",
|
||||||
pkt: &packet.ParsedPacket{IPVersion: 4, DstIP: packet.NewIP4(net.ParseIP("239.255.255.255"))},
|
pkt: &packet.Parsed{IPVersion: 4, DstIP: packet.NewIP4(net.ParseIP("239.255.255.255"))},
|
||||||
dir: out,
|
dir: out,
|
||||||
want: true,
|
want: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "v4_link_local_unicast",
|
name: "v4_link_local_unicast",
|
||||||
pkt: &packet.ParsedPacket{IPVersion: 4, DstIP: packet.NewIP4(net.ParseIP("169.254.1.2"))},
|
pkt: &packet.Parsed{IPVersion: 4, DstIP: packet.NewIP4(net.ParseIP("169.254.1.2"))},
|
||||||
dir: out,
|
dir: out,
|
||||||
want: true,
|
want: true,
|
||||||
},
|
},
|
||||||
|
@ -102,7 +102,7 @@ func newMatches4(ms []Match) (ret matches4) {
|
|||||||
|
|
||||||
// match returns whether q's source IP and destination IP:port match
|
// match returns whether q's source IP and destination IP:port match
|
||||||
// any of ms.
|
// any of ms.
|
||||||
func (ms matches4) match(q *packet.ParsedPacket) bool {
|
func (ms matches4) match(q *packet.Parsed) bool {
|
||||||
for _, m := range ms {
|
for _, m := range ms {
|
||||||
if !ip4InList(q.SrcIP, m.srcs) {
|
if !ip4InList(q.SrcIP, m.srcs) {
|
||||||
continue
|
continue
|
||||||
@ -122,7 +122,7 @@ func (ms matches4) match(q *packet.ParsedPacket) bool {
|
|||||||
|
|
||||||
// matchIPsOnly returns whether q's source and destination IP match
|
// matchIPsOnly returns whether q's source and destination IP match
|
||||||
// any of ms.
|
// any of ms.
|
||||||
func (ms matches4) matchIPsOnly(q *packet.ParsedPacket) bool {
|
func (ms matches4) matchIPsOnly(q *packet.Parsed) bool {
|
||||||
for _, m := range ms {
|
for _, m := range ms {
|
||||||
if !ip4InList(q.SrcIP, m.srcs) {
|
if !ip4InList(q.SrcIP, m.srcs) {
|
||||||
continue
|
continue
|
||||||
|
@ -45,14 +45,14 @@
|
|||||||
errOffsetTooSmall = errors.New("offset smaller than PacketStartOffset")
|
errOffsetTooSmall = errors.New("offset smaller than PacketStartOffset")
|
||||||
)
|
)
|
||||||
|
|
||||||
// parsedPacketPool holds a pool of ParsedPacket structs for use in filtering.
|
// parsedPacketPool holds a pool of Parsed structs for use in filtering.
|
||||||
// This is needed because escape analysis cannot see that parsed packets
|
// This is needed because escape analysis cannot see that parsed packets
|
||||||
// do not escape through {Pre,Post}Filter{In,Out}.
|
// do not escape through {Pre,Post}Filter{In,Out}.
|
||||||
var parsedPacketPool = sync.Pool{New: func() interface{} { return new(packet.ParsedPacket) }}
|
var parsedPacketPool = sync.Pool{New: func() interface{} { return new(packet.Parsed) }}
|
||||||
|
|
||||||
// FilterFunc is a packet-filtering function with access to the TUN device.
|
// FilterFunc is a packet-filtering function with access to the TUN device.
|
||||||
// It must not hold onto the packet struct, as its backing storage will be reused.
|
// It must not hold onto the packet struct, as its backing storage will be reused.
|
||||||
type FilterFunc func(*packet.ParsedPacket, *TUN) filter.Response
|
type FilterFunc func(*packet.Parsed, *TUN) filter.Response
|
||||||
|
|
||||||
// TUN wraps a tun.Device from wireguard-go,
|
// TUN wraps a tun.Device from wireguard-go,
|
||||||
// augmenting it with filtering and packet injection.
|
// augmenting it with filtering and packet injection.
|
||||||
@ -214,7 +214,7 @@ func (t *TUN) poll() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TUN) filterOut(p *packet.ParsedPacket) filter.Response {
|
func (t *TUN) filterOut(p *packet.Parsed) filter.Response {
|
||||||
|
|
||||||
if t.PreFilterOut != nil {
|
if t.PreFilterOut != nil {
|
||||||
if t.PreFilterOut(p, t) == filter.Drop {
|
if t.PreFilterOut(p, t) == filter.Drop {
|
||||||
@ -278,7 +278,7 @@ func (t *TUN) Read(buf []byte, offset int) (int, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p := parsedPacketPool.Get().(*packet.ParsedPacket)
|
p := parsedPacketPool.Get().(*packet.Parsed)
|
||||||
defer parsedPacketPool.Put(p)
|
defer parsedPacketPool.Put(p)
|
||||||
p.Decode(buf[offset : offset+n])
|
p.Decode(buf[offset : offset+n])
|
||||||
|
|
||||||
@ -301,7 +301,7 @@ func (t *TUN) Read(buf []byte, offset int) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *TUN) filterIn(buf []byte) filter.Response {
|
func (t *TUN) filterIn(buf []byte) filter.Response {
|
||||||
p := parsedPacketPool.Get().(*packet.ParsedPacket)
|
p := parsedPacketPool.Get().(*packet.Parsed)
|
||||||
defer parsedPacketPool.Put(p)
|
defer parsedPacketPool.Put(p)
|
||||||
p.Decode(buf)
|
p.Decode(buf)
|
||||||
|
|
||||||
|
@ -370,7 +370,7 @@ func newUserspaceEngineAdvanced(conf EngineConfig) (_ Engine, reterr error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// echoRespondToAll is an inbound post-filter responding to all echo requests.
|
// echoRespondToAll is an inbound post-filter responding to all echo requests.
|
||||||
func echoRespondToAll(p *packet.ParsedPacket, t *tstun.TUN) filter.Response {
|
func echoRespondToAll(p *packet.Parsed, t *tstun.TUN) filter.Response {
|
||||||
if p.IsEchoRequest() {
|
if p.IsEchoRequest() {
|
||||||
header := p.ICMPHeader()
|
header := p.ICMPHeader()
|
||||||
header.ToResponse()
|
header.ToResponse()
|
||||||
@ -391,7 +391,7 @@ func echoRespondToAll(p *packet.ParsedPacket, t *tstun.TUN) filter.Response {
|
|||||||
// stack, and intercepts any packets that should be handled by
|
// stack, and intercepts any packets that should be handled by
|
||||||
// tailscaled directly. Other packets are allowed to proceed into the
|
// tailscaled directly. Other packets are allowed to proceed into the
|
||||||
// main ACL filter.
|
// main ACL filter.
|
||||||
func (e *userspaceEngine) handleLocalPackets(p *packet.ParsedPacket, t *tstun.TUN) filter.Response {
|
func (e *userspaceEngine) handleLocalPackets(p *packet.Parsed, t *tstun.TUN) filter.Response {
|
||||||
if verdict := e.handleDNS(p, t); verdict == filter.Drop {
|
if verdict := e.handleDNS(p, t); verdict == filter.Drop {
|
||||||
// local DNS handled the packet.
|
// local DNS handled the packet.
|
||||||
return filter.Drop
|
return filter.Drop
|
||||||
@ -420,7 +420,7 @@ func (e *userspaceEngine) isLocalAddr(ip packet.IP4) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handleDNS is an outbound pre-filter resolving Tailscale domains.
|
// handleDNS is an outbound pre-filter resolving Tailscale domains.
|
||||||
func (e *userspaceEngine) handleDNS(p *packet.ParsedPacket, t *tstun.TUN) filter.Response {
|
func (e *userspaceEngine) handleDNS(p *packet.Parsed, t *tstun.TUN) filter.Response {
|
||||||
if p.DstIP == magicDNSIP && p.DstPort == magicDNSPort && p.IPProto == packet.UDP {
|
if p.DstIP == magicDNSIP && p.DstPort == magicDNSPort && p.IPProto == packet.UDP {
|
||||||
request := tsdns.Packet{
|
request := tsdns.Packet{
|
||||||
Payload: append([]byte(nil), p.Payload()...),
|
Payload: append([]byte(nil), p.Payload()...),
|
||||||
|
Loading…
Reference in New Issue
Block a user