Files
ctrld/cmd/cli/tunnel_owner_test.go
T
Anthony Wong 8ae09ac98e feat(cli): put the network state of the host in the log journal
The journal records the network state of the host and each change of
that state. The support team reads one file to see the interfaces, the
routes, the resolvers, and the recoveries around an incident. The same
file shows the health of the query path. A change that touches only
AirDrop or virtual interfaces runs no DNS work.
2026-09-28 16:49:05 +07:00

84 lines
2.6 KiB
Go

package cli
import (
"strings"
"testing"
)
// Captured from "ifconfig -v utunN" on macOS. A tunnel that a network extension
// owns carries an agent line with the product name in desc.
const ifconfigWindscribeTunnel = `utun4: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1420
index 22
eflags=1002080<NOAUTOIPV6LL,IPV6_ND6_ALT,TXSTART,NOACKPRI>
options=6460<TSO4,TSO6,CHANNEL_DRV,PARTIAL_CSUM,ZEROINVERT_CSUM>
inet 100.96.0.2 --> 100.96.0.2 netmask 0xffffffff
nd6 options=201<PERFORMNUD,DAD>
agent domain:NetworkExtension type:NetworkExtension flags:0xf desc:"Windscribe VPN"
agent domain:Multipath type:Path Source flags:0x3 desc:"Multipath path manager"
netif: 6d1e1b2a-4c55-4f0b-9f0a-1d2e3f4a5b6c
scheduler: FQ_CODEL
link quality: 100 (good)
`
const ifconfigTailscaleTunnel = `utun6: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1280
index 24
agent domain:NetworkExtension type:NetworkExtension flags:0xf desc:"Tailscale Tunnel"
nd6 options=201<PERFORMNUD,DAD>
`
const ifconfigSystemTunnel = `utun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500
index 16
eflags=1002080<NOAUTOIPV6LL,IPV6_ND6_ALT,TXSTART,NOACKPRI>
inet6 fe80::e1a2:3b4c:5d6e:7f80%utun0 prefixlen 64 scopeid 0x10
nd6 options=201<PERFORMNUD,DAD>
scheduler: FQ_CODEL
`
func Test_parseIfconfigAgent(t *testing.T) {
tests := []struct {
name string
output string
want string
}{
{
name: "a VPN network extension owns the tunnel",
output: ifconfigWindscribeTunnel,
want: "Windscribe VPN",
},
{
name: "a mesh client owns the tunnel",
output: ifconfigTailscaleTunnel,
want: "Tailscale Tunnel",
},
{
name: "no agent line",
output: ifconfigSystemTunnel,
},
{
name: "no output",
output: "",
},
{
name: "an agent line without a description",
output: "\tagent domain:Multipath type:Path Source flags:0x3\n\tagent domain:NetworkExtension type:NetworkExtension flags:0xf desc:\"Windscribe VPN\"\n",
want: "Windscribe VPN",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if got := parseIfconfigAgent(strings.NewReader(tc.output)); got != tc.want {
t.Fatalf("parseIfconfigAgent() = %q, want %q", got, tc.want)
}
})
}
}
// Test_parseIfconfigAgentRejectsAnOversizedLine covers a truncated read. An
// owner that the reader did not reach is not an owner that does not exist.
func Test_parseIfconfigAgentRejectsAnOversizedLine(t *testing.T) {
output := strings.Repeat("a", 70<<10) + "\n" + ifconfigWindscribeTunnel
if got := parseIfconfigAgent(strings.NewReader(output)); got != "" {
t.Fatalf("tunnel owner of a truncated read = %q, want an empty owner", got)
}
}