package outward import ( "os" "path/filepath" "reflect" "testing" ) // A routing table as the kernel writes it: a default route, a route to the zero address that is not // one, and a route on the loopback. Only the default route's link faces outside. const routeV4 = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT enp9s0 00000000 01FEA8C0 0003 0 0 100 00000000 0 0 0 docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0 enp9s0 00000000 00000000 0001 0 0 100 00FFFFFF 0 0 0 lo 00000000 00000000 0003 0 0 0 00000000 0 0 0 ` const routeV6 = `00000000000000000000000000000000 00 00000000000000000000000000000000 00 fe800000000000000000000000000001 00000400 00000001 00000000 00000003 wlan0 fd0000000000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 enp9s0 ` func write(t *testing.T, dir, name, body string) { t.Helper() if err := os.WriteFile(filepath.Join(dir, name), []byte(body), 0o644); err != nil { t.Fatal(err) } } func TestLinksAreTheOnesCarryingADefaultRoute(t *testing.T) { dir := t.TempDir() write(t, dir, "route", routeV4) write(t, dir, "ipv6_route", routeV6) got, err := Links(dir) if err != nil { t.Fatal(err) } // The cable from the v4 table and the radio from the v6 one. Not docker0, whose route is not a // default; not the loopback, which faces nothing; and not the v6 route with a real prefix. want := []string{"enp9s0", "wlan0"} if !reflect.DeepEqual(got, want) { t.Fatalf("outward links are %v, want %v", got, want) } } // A route to the zero address with a real mask is not a default route. Trusting the destination // alone would name every link with such a route as facing outside, and a filter that treats an // internal bridge as outward constrains this machine's own guests — the fault ADR 0140 removes. func TestAZeroDestinationWithAMaskIsNotADefaultRoute(t *testing.T) { dir := t.TempDir() write(t, dir, "route", `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT br-abc 00000000 00000000 0001 0 0 0 00FFFFFF 0 0 0 `) got, err := Links(dir) if err != nil { t.Fatal(err) } if len(got) != 0 { t.Fatalf("outward links are %v, want none", got) } } // A machine with no route off itself says so, rather than guessing. The mesh refuses to compose a // filter for it; a rule written around a link with no name does not load, and a rule set that does // not load is a machine filtering nothing while its unit reports success. func TestNoDefaultRouteIsNoLinks(t *testing.T) { dir := t.TempDir() write(t, dir, "route", "Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT\n") got, err := Links(dir) if err != nil { t.Fatal(err) } if len(got) != 0 { t.Fatalf("outward links are %v, want none", got) } } // A machine without the second address family has no file for it. That is not a machine that cannot // be filtered, so a missing table is read as no routes rather than as a failure. func TestAMissingTableIsNotAFailure(t *testing.T) { dir := t.TempDir() write(t, dir, "route", routeV4) got, err := Links(dir) if err != nil { t.Fatalf("a missing v6 table should not fail: %v", err) } if !reflect.DeepEqual(got, []string{"enp9s0"}) { t.Fatalf("outward links are %v, want [enp9s0]", got) } } // The same link carrying a default route in both families is reported once. func TestALinkIsReportedOnce(t *testing.T) { dir := t.TempDir() write(t, dir, "route", routeV4) write(t, dir, "ipv6_route", "00000000000000000000000000000000 00 00000000000000000000000000000000 00 "+ "fe800000000000000000000000000001 00000400 00000001 00000000 00000003 enp9s0\n") got, err := Links(dir) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(got, []string{"enp9s0"}) { t.Fatalf("outward links are %v, want [enp9s0]", got) } } // Against this machine's own routing table, so the parse is held to what the kernel actually writes // and not only to a fixture written to agree with it. func TestAgainstThisMachinesOwnTable(t *testing.T) { got, err := Links("") if err != nil { t.Fatal(err) } if len(got) == 0 { t.Skip("this machine has no default route") } t.Logf("this machine's outward links: %v", got) }