Skip to content

Commit 9f25ec7

Browse files
Add support for GCP extensions
The support is modeled on azure.go under tlvparse. The GCP spec is similar but encodes uint64. There's no subtype. Since zero value of uint64 is technically a valid header value, I've opted for an extra boolean to indicate if it was found (same as with Azure).
1 parent 3aa7ea9 commit 9f25ec7

File tree

2 files changed

+129
-0
lines changed

2 files changed

+129
-0
lines changed

tlvparse/gcp.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package tlvparse
2+
3+
import (
4+
"encoding/binary"
5+
6+
"github.com/pires/go-proxyproto"
7+
)
8+
9+
const (
10+
// PP2_TYPE_GCP indicates a Google Cloud Platform header
11+
PP2_TYPE_GCP proxyproto.PP2Type = 0xE0
12+
)
13+
14+
// ExtractPSCConnectionID returns the first PSC Connection ID in the TLV if it exists and is well-formed and
15+
// a bool indicating one was found.
16+
func ExtractPSCConnectionID(tlvs []proxyproto.TLV) (uint64, bool) {
17+
for _, tlv := range tlvs {
18+
if linkID, err := pscConnectionID(tlv); err == nil {
19+
return linkID, true
20+
}
21+
}
22+
return 0, false
23+
}
24+
25+
// pscConnectionID returns the ID of a GCP PSC extension TLV or errors with ErrIncompatibleTLV or
26+
// ErrMalformedTLV if it's the wrong TLV type or is malformed.
27+
//
28+
// Field Length (bytes) Description
29+
// Type 1 PP2_TYPE_GCP (0xE0)
30+
// Length 2 Length of value (always 0x0008)
31+
// Value 8 The 8-byte PSC Connection ID (decode to uint64; big endian)
32+
//
33+
// For example proxyproto.TLV{Type:0xea, Length:8, Value:[]byte{0xff, 0xff, 0xff, 0xff, 0xc0, 0xa8, 0x64, 0x02}}
34+
// will be decoded as 18446744072646845442.
35+
//
36+
// See https://cloud.google.com/vpc/docs/configure-private-service-connect-producer
37+
func pscConnectionID(t proxyproto.TLV) (uint64, error) {
38+
if !isPSCConnectionID(t) {
39+
return 0, proxyproto.ErrIncompatibleTLV
40+
}
41+
linkID := binary.BigEndian.Uint64(t.Value)
42+
return linkID, nil
43+
}
44+
45+
func isPSCConnectionID(t proxyproto.TLV) bool {
46+
return t.Type == PP2_TYPE_GCP && len(t.Value) == 8
47+
}

tlvparse/gcp_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package tlvparse
2+
3+
import (
4+
"testing"
5+
6+
"github.com/pires/go-proxyproto"
7+
)
8+
9+
func TestExtractPSCConnectionID(t *testing.T) {
10+
tests := []struct {
11+
name string
12+
tlvs []proxyproto.TLV
13+
wantPSCConnectionID uint64
14+
wantFound bool
15+
}{
16+
{
17+
name: "nil TLVs",
18+
tlvs: nil,
19+
wantFound: false,
20+
},
21+
{
22+
name: "empty TLVs",
23+
tlvs: []proxyproto.TLV{},
24+
wantFound: false,
25+
},
26+
{
27+
name: "AWS VPC endpoint ID",
28+
tlvs: []proxyproto.TLV{
29+
{
30+
Type: 0xEA,
31+
Value: []byte{0x01, 0x76, 0x70, 0x63, 0x65, 0x2d, 0x61, 0x62, 0x63, 0x31, 0x32, 0x33},
32+
},
33+
},
34+
wantFound: false,
35+
},
36+
{
37+
name: "GCP link ID",
38+
tlvs: []proxyproto.TLV{
39+
{
40+
Type: PP2_TYPE_GCP,
41+
Value: []byte{'\xff', '\xff', '\xff', '\xff', '\xc0', '\xa8', '\x64', '\x02'},
42+
},
43+
},
44+
wantPSCConnectionID: 18446744072646845442,
45+
wantFound: true,
46+
},
47+
{
48+
name: "Multiple TLVs",
49+
tlvs: []proxyproto.TLV{
50+
{ // AWS
51+
Type: 0xEA,
52+
Value: []byte{0x01, 0x76, 0x70, 0x63, 0x65, 0x2d, 0x61, 0x62, 0x63, 0x31, 0x32, 0x33},
53+
},
54+
{ // Azure
55+
Type: 0xEE,
56+
Value: []byte{0x02, 0x01, 0x01, 0x01, 0x01},
57+
},
58+
{ // GCP but wrong length
59+
Type: 0xE0,
60+
Value: []byte{0xff, 0xff, 0xff},
61+
},
62+
{ // Correct
63+
Type: 0xE0,
64+
Value: []byte{'\xff', '\xff', '\xff', '\xff', '\xc0', '\xa8', '\x64', '\x02'},
65+
},
66+
},
67+
wantPSCConnectionID: 18446744072646845442,
68+
wantFound: true,
69+
},
70+
}
71+
for _, tt := range tests {
72+
t.Run(tt.name, func(t *testing.T) {
73+
linkID, hasLinkID := ExtractPSCConnectionID(tt.tlvs)
74+
if hasLinkID != tt.wantFound {
75+
t.Errorf("ExtractPSCConnectionID() got1 = %v, want %v", hasLinkID, tt.wantFound)
76+
}
77+
if linkID != tt.wantPSCConnectionID {
78+
t.Errorf("ExtractPSCConnectionID() got = %v, want %v", linkID, tt.wantPSCConnectionID)
79+
}
80+
})
81+
}
82+
}

0 commit comments

Comments
 (0)