Repository navigation
Expand file tree
/
Copy pathpmtu_test.go
More file actions
109 lines (104 loc) · 4.69 KB
/
Copy pathpmtu_test.go
File metadata and controls
109 lines (104 loc) · 4.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package gvisorinterop_test
import (
"context"
"errors"
"net/netip"
"syscall"
"testing"
"time"
"github.com/metacubex/mipstack"
)
// TestPathMTUDiscoveryInterop verifies router-generated PMTU feedback and
// Linux IP_PMTUDISC_DO behavior. The IPv6 branch separately documents and
// rejects the pinned gVisor implementation's invalid zero-MTU feedback.
func TestPathMTUDiscoveryInterop(t *testing.T) {
for _, family := range interopFamilies {
family := family
for _, nextHopMTU := range interopPathMTUsForFamily(family) {
nextHopMTU := nextHopMTU
t.Run(family.name+"/"+interopMTUName(nextHopMTU), func(t *testing.T) {
network := newInteropNetworkWithOptions(t, interopNetworkOptions{
families: []interopFamily{family}, mtu: 1500, gvisorMTU: nextHopMTU, forwarding: true,
})
ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second)
defer cancel()
target := netipAddrPort(family.forwardAddress, 46001)
netConnection, err := network.mipstack.DialUDP(ctx, family.udpNetwork, netip.AddrPort{}, target)
if err != nil {
t.Fatalf("dial mipstack PMTU probe socket: %v", err)
}
connection := netConnection.(*mipstack.UDPConn)
defer connection.Close()
if err = connection.SetDeadline(time.Now().Add(6 * time.Second)); err != nil {
t.Fatalf("set mipstack PMTU socket deadline: %v", err)
}
if err = connection.SetPathMTUDiscovery(mipstack.PathMTUDiscoveryDo); err != nil {
t.Fatalf("enable mipstack PMTU discovery: %v", err)
}
headerSize := 28
if family.mipstackAddress.Is6() {
headerSize = 48
}
fittingSize := int(nextHopMTU) - headerSize
oversized := patternedPayload(fittingSize+1, 241)
written, err := connection.Write(oversized)
if err != nil || written != len(oversized) {
t.Fatalf("write initial oversized PMTU datagram: n=%d, error=%v", written, err)
}
if _, err = connection.Read(make([]byte, 1)); err == nil {
t.Fatal("read after gVisor PMTU error succeeded")
}
var networkError mipstack.ICMPError
if !errors.As(err, &networkError) {
t.Fatalf("mipstack PMTU error = %T %v, want ICMPError", err, err)
}
wantType, wantCode := byte(3), byte(4)
if family.mipstackAddress.Is6() {
wantType, wantCode = 2, 0
}
if networkError.Reporter != family.gvisorAddress || networkError.Type != wantType || networkError.Code != wantCode ||
networkError.QuotedSource != family.mipstackAddress || networkError.QuotedTarget != family.forwardAddress {
t.Fatalf("mipstack PMTU ICMP error = reporter %v, type/code %d/%d, MTU %d, quote %v -> %v", networkError.Reporter,
networkError.Type, networkError.Code, networkError.MTU, networkError.QuotedSource, networkError.QuotedTarget)
}
if family.mipstackAddress.Is6() && networkError.MTU == 0 {
// The pinned gVisor forwarder constructs icmpReasonPacketTooBig
// without the outgoing link MTU and therefore serializes zero in
// the type-specific field. RFC 4443 section 3.2 requires the
// next-hop MTU. Preserve parsing coverage and verify that mipstack
// rejects the invalid update, but do not weaken the valid-PTB
// assertions to accommodate the reference-stack defect.
if pathMTU, pathErr := network.mipstack.PathMTU(family.forwardAddress); pathErr != nil || pathMTU != 1500 {
t.Fatalf("mipstack accepted invalid zero IPv6 PMTU: MTU=%d, error=%v", pathMTU, pathErr)
}
t.Skip("pinned gVisor emits an RFC 4443 Packet Too Big message with MTU zero")
}
if networkError.MTU != nextHopMTU {
t.Fatalf("mipstack PMTU error MTU = %d, want %d", networkError.MTU, nextHopMTU)
}
if pathMTU, pathErr := network.mipstack.PathMTU(family.forwardAddress); pathErr != nil || pathMTU != int(nextHopMTU) {
t.Fatalf("mipstack learned PMTU = %d, error=%v; want %d", pathMTU, pathErr, nextHopMTU)
}
if _, err = connection.Write(oversized); !errors.Is(err, syscall.EMSGSIZE) {
t.Fatalf("write above learned PMTU = %v, want EMSGSIZE", err)
}
fitting := patternedPayload(fittingSize, 251)
if written, err = connection.Write(fitting); err != nil || written != len(fitting) {
t.Fatalf("write below learned PMTU: n=%d, error=%v", written, err)
}
if info := connection.Info(); info.PathMTU != int(nextHopMTU) || info.ICMPErrors != 1 {
t.Fatalf("mipstack UDP PMTU info = %+v", info)
}
})
}
}
}
// interopPathMTUsForFamily returns lower next-hop MTUs that leave room for a
// one-packet probe under the 1,500-byte ingress link. IPv6 excludes values
// below its RFC 8200 minimum.
func interopPathMTUsForFamily(family interopFamily) []uint32 {
if family.mipstackAddress.Is4() {
return []uint32{68, 576, 1280, 1420}
}
return []uint32{1280, 1420}
}