big_send now forces the Don't-Fragment bit for the whole burst by default, so the largest size that arrives IS the downstream path MTU rather than "fragments got through" — two different measurements the schema already separates. Sizes above our own egress MTU (from the startup self-test) are refused up front and reported as max_df_bytes, because absence caused by our kernel must not be read as a limit of the client's path. Uploads: one JSON file per run under the state dir, with the policy the operator actually cares about — who may upload (off / anonymous / account), how large, how long to keep, and the least anonymization accepted. The profile advertises all of it so the app can present the switch honestly instead of discovering the rules by failing. `account` refuses today rather than falling back to anonymous: picking the strict setting before OIDC lands must not silently mean the loose one. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
53 lines
1.6 KiB
Go
53 lines
1.6 KiB
Go
// SPDX-FileCopyrightText: 2026 Echolot contributors
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package control
|
|
|
|
import (
|
|
"net/netip"
|
|
"testing"
|
|
"time"
|
|
|
|
"echo-lot.app/server/internal/session"
|
|
)
|
|
|
|
// The DF ceiling is the difference between "the client's path cannot carry this" and "we could
|
|
// never have sent it in the first place". Getting the header arithmetic wrong would silently
|
|
// attribute a server limit to the client's network, so it is pinned here.
|
|
func TestMaxDFPayload(t *testing.T) {
|
|
mgr := session.NewManager(time.Minute)
|
|
|
|
newSess := func(src string) *session.Session {
|
|
s, _, err := mgr.New("dev", "cred", netip.MustParseAddr("203.0.113.9"))
|
|
if err != nil {
|
|
t.Fatalf("new session: %v", err)
|
|
}
|
|
if src != "" {
|
|
s.NoteDataSource(netip.MustParseAddrPort(src))
|
|
}
|
|
return s
|
|
}
|
|
|
|
cases := []struct {
|
|
name string
|
|
mtu func() int
|
|
src string
|
|
want int
|
|
}{
|
|
{"no egress mtu hook means no clamp", nil, "198.51.100.4:5000", 0},
|
|
{"unknown egress mtu means no clamp", func() int { return 0 }, "198.51.100.4:5000", 0},
|
|
{"ipv4 subtracts ip+udp", func() int { return 1500 }, "198.51.100.4:5000", 1472},
|
|
{"ipv6 subtracts the larger header", func() int { return 1500 }, "[2001:db8::4]:5000", 1452},
|
|
{"pppoe-style 1492 egress", func() int { return 1492 }, "198.51.100.4:5000", 1464},
|
|
{"no data source yet falls back to ipv4 overhead", func() int { return 1500 }, "", 1472},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
s := &Server{EgressMTU: tc.mtu}
|
|
if got := s.maxDFPayload(newSess(tc.src)); got != tc.want {
|
|
t.Fatalf("maxDFPayload = %d, want %d", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|