| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026 |
- // Copyright (c) Tailscale Inc & AUTHORS
- // SPDX-License-Identifier: BSD-3-Clause
- //go:build linux
- package linuxfw
- import (
- "bytes"
- "errors"
- "fmt"
- "net/netip"
- "os"
- "runtime"
- "strings"
- "testing"
- "github.com/google/nftables"
- "github.com/google/nftables/expr"
- "github.com/mdlayher/netlink"
- "github.com/vishvananda/netns"
- "tailscale.com/net/tsaddr"
- "tailscale.com/tstest"
- "tailscale.com/types/logger"
- )
- // nfdump returns a hexdump of 4 bytes per line (like nft --debug=all), allowing
- // users to make sense of large byte literals more easily.
- func nfdump(b []byte) string {
- var buf bytes.Buffer
- i := 0
- for ; i < len(b); i += 4 {
- // TODO: show printable characters as ASCII
- fmt.Fprintf(&buf, "%02x %02x %02x %02x\n",
- b[i],
- b[i+1],
- b[i+2],
- b[i+3])
- }
- for ; i < len(b); i++ {
- fmt.Fprintf(&buf, "%02x ", b[i])
- }
- return buf.String()
- }
- func TestMaskof(t *testing.T) {
- pfx, err := netip.ParsePrefix("192.168.1.0/24")
- if err != nil {
- t.Fatal(err)
- }
- want := []byte{0xff, 0xff, 0xff, 0x00}
- if got := maskof(pfx); !bytes.Equal(got, want) {
- t.Errorf("got %v; want %v", got, want)
- }
- }
- // linediff returns a side-by-side diff of two nfdump() return values, flagging
- // lines which are not equal with an exclamation point prefix.
- func linediff(a, b string) string {
- var buf bytes.Buffer
- fmt.Fprintf(&buf, "got -- want\n")
- linesA := strings.Split(a, "\n")
- linesB := strings.Split(b, "\n")
- for idx, lineA := range linesA {
- if idx >= len(linesB) {
- break
- }
- lineB := linesB[idx]
- prefix := "! "
- if lineA == lineB {
- prefix = " "
- }
- fmt.Fprintf(&buf, "%s%s -- %s\n", prefix, lineA, lineB)
- }
- return buf.String()
- }
- func newTestConn(t *testing.T, want [][]byte) *nftables.Conn {
- conn, err := nftables.New(nftables.WithTestDial(
- func(req []netlink.Message) ([]netlink.Message, error) {
- for idx, msg := range req {
- b, err := msg.MarshalBinary()
- if err != nil {
- t.Fatal(err)
- }
- if len(b) < 16 {
- continue
- }
- b = b[16:]
- if len(want) == 0 {
- t.Errorf("no want entry for message %d: %x", idx, b)
- continue
- }
- if got, want := b, want[0]; !bytes.Equal(got, want) {
- t.Errorf("message %d: %s", idx, linediff(nfdump(got), nfdump(want)))
- }
- want = want[1:]
- }
- return req, nil
- }))
- if err != nil {
- t.Fatal(err)
- }
- return conn
- }
- func TestInsertHookRule(t *testing.T) {
- proto := nftables.TableFamilyIPv4
- want := [][]byte{
- // batch begin
- []byte("\x00\x00\x00\x0a"),
- // nft add table ip ts-filter-test
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x08\x00\x02\x00\x00\x00\x00\x00"),
- // nft add chain ip ts-filter-test ts-input-test { type filter hook input priority 0 \; }
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x12\x00\x03\x00\x74\x73\x2d\x69\x6e\x70\x75\x74\x2d\x74\x65\x73\x74\x00\x00\x00\x14\x00\x04\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0b\x00\x07\x00\x66\x69\x6c\x74\x65\x72\x00\x00"),
- // nft add chain ip ts-filter-test ts-jumpto
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x0e\x00\x03\x00\x74\x73\x2d\x6a\x75\x6d\x70\x74\x6f\x00\x00\x00"),
- // nft add rule ip ts-filter-test ts-input-test counter jump ts-jumptp
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x12\x00\x02\x00\x74\x73\x2d\x69\x6e\x70\x75\x74\x2d\x74\x65\x73\x74\x00\x00\x00\x70\x00\x04\x80\x2c\x00\x01\x80\x0c\x00\x01\x00\x63\x6f\x75\x6e\x74\x65\x72\x00\x1c\x00\x02\x80\x0c\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x2c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x00\x20\x00\x02\x80\x1c\x00\x02\x80\x08\x00\x01\x00\xff\xff\xff\xfd\x0e\x00\x02\x00\x74\x73\x2d\x6a\x75\x6d\x70\x74\x6f\x00\x00\x00"),
- // batch end
- []byte("\x00\x00\x00\x0a"),
- }
- testConn := newTestConn(t, want)
- table := testConn.AddTable(&nftables.Table{
- Family: proto,
- Name: "ts-filter-test",
- })
- fromchain := testConn.AddChain(&nftables.Chain{
- Name: "ts-input-test",
- Table: table,
- Type: nftables.ChainTypeFilter,
- Hooknum: nftables.ChainHookInput,
- Priority: nftables.ChainPriorityFilter,
- })
- tochain := testConn.AddChain(&nftables.Chain{
- Name: "ts-jumpto",
- Table: table,
- })
- err := addHookRule(testConn, table, fromchain, tochain.Name)
- if err != nil {
- t.Fatal(err)
- }
- }
- func TestInsertLoopbackRule(t *testing.T) {
- proto := nftables.TableFamilyIPv4
- want := [][]byte{
- // batch begin
- []byte("\x00\x00\x00\x0a"),
- // nft add table ip ts-filter-test
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x08\x00\x02\x00\x00\x00\x00\x00"),
- // nft add chain ip ts-filter-test ts-input-test { type filter hook input priority 0 \; }
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x12\x00\x03\x00\x74\x73\x2d\x69\x6e\x70\x75\x74\x2d\x74\x65\x73\x74\x00\x00\x00\x14\x00\x04\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0b\x00\x07\x00\x66\x69\x6c\x74\x65\x72\x00\x00"),
- // nft add rule ip ts-filter-test ts-input-test iifname "lo" ip saddr 192.168.0.2 counter accept
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x12\x00\x02\x00\x74\x73\x2d\x69\x6e\x70\x75\x74\x2d\x74\x65\x73\x74\x00\x00\x00\x10\x01\x04\x80\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x06\x08\x00\x01\x00\x00\x00\x00\x01\x2c\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x20\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0c\x00\x03\x80\x06\x00\x01\x00\x6c\x6f\x00\x00\x34\x00\x01\x80\x0c\x00\x01\x00\x70\x61\x79\x6c\x6f\x61\x64\x00\x24\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x01\x08\x00\x03\x00\x00\x00\x00\x0c\x08\x00\x04\x00\x00\x00\x00\x04\x2c\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x20\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0c\x00\x03\x80\x08\x00\x01\x00\xc0\xa8\x00\x02\x2c\x00\x01\x80\x0c\x00\x01\x00\x63\x6f\x75\x6e\x74\x65\x72\x00\x1c\x00\x02\x80\x0c\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x30\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x1c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x00\x10\x00\x02\x80\x0c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01"),
- // batch end
- []byte("\x00\x00\x00\x0a"),
- }
- testConn := newTestConn(t, want)
- table := testConn.AddTable(&nftables.Table{
- Family: proto,
- Name: "ts-filter-test",
- })
- chain := testConn.AddChain(&nftables.Chain{
- Name: "ts-input-test",
- Table: table,
- Type: nftables.ChainTypeFilter,
- Hooknum: nftables.ChainHookInput,
- Priority: nftables.ChainPriorityFilter,
- })
- addr := netip.MustParseAddr("192.168.0.2")
- err := insertLoopbackRule(testConn, proto, table, chain, addr)
- if err != nil {
- t.Fatal(err)
- }
- }
- func TestInsertLoopbackRuleV6(t *testing.T) {
- protoV6 := nftables.TableFamilyIPv6
- want := [][]byte{
- // batch begin
- []byte("\x00\x00\x00\x0a"),
- // nft add table ip6 ts-filter-test
- []byte("\x0a\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x08\x00\x02\x00\x00\x00\x00\x00"),
- // nft add chain ip6 ts-filter-test ts-input-test { type filter hook input priority 0\; }
- []byte("\x0a\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x12\x00\x03\x00\x74\x73\x2d\x69\x6e\x70\x75\x74\x2d\x74\x65\x73\x74\x00\x00\x00\x14\x00\x04\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0b\x00\x07\x00\x66\x69\x6c\x74\x65\x72\x00\x00"),
- // nft add rule ip6 ts-filter-test ts-input-test iifname "lo" ip6 addr 2001:db8::1 counter accept
- []byte("\x0a\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x12\x00\x02\x00\x74\x73\x2d\x69\x6e\x70\x75\x74\x2d\x74\x65\x73\x74\x00\x00\x00\x1c\x01\x04\x80\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x06\x08\x00\x01\x00\x00\x00\x00\x01\x2c\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x20\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0c\x00\x03\x80\x06\x00\x01\x00\x6c\x6f\x00\x00\x34\x00\x01\x80\x0c\x00\x01\x00\x70\x61\x79\x6c\x6f\x61\x64\x00\x24\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x01\x08\x00\x03\x00\x00\x00\x00\x08\x08\x00\x04\x00\x00\x00\x00\x10\x38\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x2c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x18\x00\x03\x80\x14\x00\x01\x00\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x2c\x00\x01\x80\x0c\x00\x01\x00\x63\x6f\x75\x6e\x74\x65\x72\x00\x1c\x00\x02\x80\x0c\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x30\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x1c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x00\x10\x00\x02\x80\x0c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01"),
- // batch end
- []byte("\x00\x00\x00\x0a"),
- }
- testConn := newTestConn(t, want)
- tableV6 := testConn.AddTable(&nftables.Table{
- Family: protoV6,
- Name: "ts-filter-test",
- })
- chainV6 := testConn.AddChain(&nftables.Chain{
- Name: "ts-input-test",
- Table: tableV6,
- Type: nftables.ChainTypeFilter,
- Hooknum: nftables.ChainHookInput,
- Priority: nftables.ChainPriorityFilter,
- })
- addrV6 := netip.MustParseAddr("2001:db8::1")
- err := insertLoopbackRule(testConn, protoV6, tableV6, chainV6, addrV6)
- if err != nil {
- t.Fatal(err)
- }
- }
- func TestAddReturnChromeOSVMRangeRule(t *testing.T) {
- proto := nftables.TableFamilyIPv4
- want := [][]byte{
- // batch begin
- []byte("\x00\x00\x00\x0a"),
- // nft add table ip ts-filter-test
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x08\x00\x02\x00\x00\x00\x00\x00"),
- // nft add chain ip ts-filter-test ts-input-test { type filter hook input priority 0\; }
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x12\x00\x03\x00\x74\x73\x2d\x69\x6e\x70\x75\x74\x2d\x74\x65\x73\x74\x00\x00\x00\x14\x00\x04\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0b\x00\x07\x00\x66\x69\x6c\x74\x65\x72\x00\x00"),
- // nft add rule ip ts-filter-test ts-input-test iifname != "testTunn" ip saddr 100.115.92.0/23 counter return
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x12\x00\x02\x00\x74\x73\x2d\x69\x6e\x70\x75\x74\x2d\x74\x65\x73\x74\x00\x00\x00\x58\x01\x04\x80\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x06\x08\x00\x01\x00\x00\x00\x00\x01\x30\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x24\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x01\x10\x00\x03\x80\x0c\x00\x01\x00\x74\x65\x73\x74\x54\x75\x6e\x6e\x34\x00\x01\x80\x0c\x00\x01\x00\x70\x61\x79\x6c\x6f\x61\x64\x00\x24\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x01\x08\x00\x03\x00\x00\x00\x00\x0c\x08\x00\x04\x00\x00\x00\x00\x04\x44\x00\x01\x80\x0c\x00\x01\x00\x62\x69\x74\x77\x69\x73\x65\x00\x34\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x01\x08\x00\x03\x00\x00\x00\x00\x04\x0c\x00\x04\x80\x08\x00\x01\x00\xff\xff\xfe\x00\x0c\x00\x05\x80\x08\x00\x01\x00\x00\x00\x00\x00\x2c\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x20\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0c\x00\x03\x80\x08\x00\x01\x00\x64\x73\x5c\x00\x2c\x00\x01\x80\x0c\x00\x01\x00\x63\x6f\x75\x6e\x74\x65\x72\x00\x1c\x00\x02\x80\x0c\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x30\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x1c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x00\x10\x00\x02\x80\x0c\x00\x02\x80\x08\x00\x01\x00\xff\xff\xff\xfb"),
- // batch end
- []byte("\x00\x00\x00\x0a"),
- }
- testConn := newTestConn(t, want)
- table := testConn.AddTable(&nftables.Table{
- Family: proto,
- Name: "ts-filter-test",
- })
- chain := testConn.AddChain(&nftables.Chain{
- Name: "ts-input-test",
- Table: table,
- Type: nftables.ChainTypeFilter,
- Hooknum: nftables.ChainHookInput,
- Priority: nftables.ChainPriorityFilter,
- })
- err := addReturnChromeOSVMRangeRule(testConn, table, chain, "testTunn")
- if err != nil {
- t.Fatal(err)
- }
- }
- func TestAddDropCGNATRangeRule(t *testing.T) {
- proto := nftables.TableFamilyIPv4
- want := [][]byte{
- // batch begin
- []byte("\x00\x00\x00\x0a"),
- // nft add table ip ts-filter-test
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x08\x00\x02\x00\x00\x00\x00\x00"),
- // nft add chain ip ts-filter-test ts-input-test { type filter hook input priority filter; }
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x12\x00\x03\x00\x74\x73\x2d\x69\x6e\x70\x75\x74\x2d\x74\x65\x73\x74\x00\x00\x00\x14\x00\x04\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0b\x00\x07\x00\x66\x69\x6c\x74\x65\x72\x00\x00"),
- // nft add rule ip ts-filter-test ts-input-test iifname != "testTunn" ip saddr 100.64.0.0/10 counter drop
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x12\x00\x02\x00\x74\x73\x2d\x69\x6e\x70\x75\x74\x2d\x74\x65\x73\x74\x00\x00\x00\x58\x01\x04\x80\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x06\x08\x00\x01\x00\x00\x00\x00\x01\x30\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x24\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x01\x10\x00\x03\x80\x0c\x00\x01\x00\x74\x65\x73\x74\x54\x75\x6e\x6e\x34\x00\x01\x80\x0c\x00\x01\x00\x70\x61\x79\x6c\x6f\x61\x64\x00\x24\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x01\x08\x00\x03\x00\x00\x00\x00\x0c\x08\x00\x04\x00\x00\x00\x00\x04\x44\x00\x01\x80\x0c\x00\x01\x00\x62\x69\x74\x77\x69\x73\x65\x00\x34\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x01\x08\x00\x03\x00\x00\x00\x00\x04\x0c\x00\x04\x80\x08\x00\x01\x00\xff\xc0\x00\x00\x0c\x00\x05\x80\x08\x00\x01\x00\x00\x00\x00\x00\x2c\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x20\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0c\x00\x03\x80\x08\x00\x01\x00\x64\x40\x00\x00\x2c\x00\x01\x80\x0c\x00\x01\x00\x63\x6f\x75\x6e\x74\x65\x72\x00\x1c\x00\x02\x80\x0c\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x30\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x1c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x00\x10\x00\x02\x80\x0c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x00"),
- // batch end
- []byte("\x00\x00\x00\x0a"),
- }
- testConn := newTestConn(t, want)
- table := testConn.AddTable(&nftables.Table{
- Family: proto,
- Name: "ts-filter-test",
- })
- chain := testConn.AddChain(&nftables.Chain{
- Name: "ts-input-test",
- Table: table,
- Type: nftables.ChainTypeFilter,
- Hooknum: nftables.ChainHookInput,
- Priority: nftables.ChainPriorityFilter,
- })
- err := addDropCGNATRangeRule(testConn, table, chain, "testTunn")
- if err != nil {
- t.Fatal(err)
- }
- }
- func TestAddSetSubnetRouteMarkRule(t *testing.T) {
- proto := nftables.TableFamilyIPv4
- want := [][]byte{
- // batch begin
- []byte("\x00\x00\x00\x0a"),
- // nft add table ip ts-filter-test
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x08\x00\x02\x00\x00\x00\x00\x00"),
- // nft add chain ip ts-filter-test ts-forward-test { type filter hook forward priority 0\; }
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x14\x00\x03\x00\x74\x73\x2d\x66\x6f\x72\x77\x61\x72\x64\x2d\x74\x65\x73\x74\x00\x14\x00\x04\x80\x08\x00\x01\x00\x00\x00\x00\x02\x08\x00\x02\x00\x00\x00\x00\x00\x0b\x00\x07\x00\x66\x69\x6c\x74\x65\x72\x00\x00"),
- // nft add rule ip ts-filter-test ts-forward-test iifname "testTunn" counter meta mark set mark and 0xff00ffff xor 0x40000
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x14\x00\x02\x00\x74\x73\x2d\x66\x6f\x72\x77\x61\x72\x64\x2d\x74\x65\x73\x74\x00\x10\x01\x04\x80\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x06\x08\x00\x01\x00\x00\x00\x00\x01\x30\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x24\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x10\x00\x03\x80\x0c\x00\x01\x00\x74\x65\x73\x74\x54\x75\x6e\x6e\x2c\x00\x01\x80\x0c\x00\x01\x00\x63\x6f\x75\x6e\x74\x65\x72\x00\x1c\x00\x02\x80\x0c\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x03\x08\x00\x01\x00\x00\x00\x00\x01\x44\x00\x01\x80\x0c\x00\x01\x00\x62\x69\x74\x77\x69\x73\x65\x00\x34\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x01\x08\x00\x03\x00\x00\x00\x00\x04\x0c\x00\x04\x80\x08\x00\x01\x00\xff\x00\xff\xff\x0c\x00\x05\x80\x08\x00\x01\x00\x00\x04\x00\x00\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x03\x08\x00\x03\x00\x00\x00\x00\x01"),
- // batch end
- []byte("\x00\x00\x00\x0a"),
- }
- testConn := newTestConn(t, want)
- table := testConn.AddTable(&nftables.Table{
- Family: proto,
- Name: "ts-filter-test",
- })
- chain := testConn.AddChain(&nftables.Chain{
- Name: "ts-forward-test",
- Table: table,
- Type: nftables.ChainTypeFilter,
- Hooknum: nftables.ChainHookForward,
- Priority: nftables.ChainPriorityFilter,
- })
- err := addSetSubnetRouteMarkRule(testConn, table, chain, "testTunn")
- if err != nil {
- t.Fatal(err)
- }
- }
- func TestAddDropOutgoingPacketFromCGNATRangeRuleWithTunname(t *testing.T) {
- proto := nftables.TableFamilyIPv4
- want := [][]byte{
- // batch begin
- []byte("\x00\x00\x00\x0a"),
- // nft add table ip ts-filter-test
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x08\x00\x02\x00\x00\x00\x00\x00"),
- // nft add chain ip ts-filter-test ts-forward-test { type filter hook forward priority 0\; }
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x14\x00\x03\x00\x74\x73\x2d\x66\x6f\x72\x77\x61\x72\x64\x2d\x74\x65\x73\x74\x00\x14\x00\x04\x80\x08\x00\x01\x00\x00\x00\x00\x02\x08\x00\x02\x00\x00\x00\x00\x00\x0b\x00\x07\x00\x66\x69\x6c\x74\x65\x72\x00\x00"),
- // nft add rule ip ts-filter-test ts-forward-test oifname "testTunn" ip saddr 100.64.0.0/10 counter drop
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x14\x00\x02\x00\x74\x73\x2d\x66\x6f\x72\x77\x61\x72\x64\x2d\x74\x65\x73\x74\x00\x58\x01\x04\x80\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x07\x08\x00\x01\x00\x00\x00\x00\x01\x30\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x24\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x10\x00\x03\x80\x0c\x00\x01\x00\x74\x65\x73\x74\x54\x75\x6e\x6e\x34\x00\x01\x80\x0c\x00\x01\x00\x70\x61\x79\x6c\x6f\x61\x64\x00\x24\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x01\x08\x00\x03\x00\x00\x00\x00\x0c\x08\x00\x04\x00\x00\x00\x00\x04\x44\x00\x01\x80\x0c\x00\x01\x00\x62\x69\x74\x77\x69\x73\x65\x00\x34\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x01\x08\x00\x03\x00\x00\x00\x00\x04\x0c\x00\x04\x80\x08\x00\x01\x00\xff\xc0\x00\x00\x0c\x00\x05\x80\x08\x00\x01\x00\x00\x00\x00\x00\x2c\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x20\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0c\x00\x03\x80\x08\x00\x01\x00\x64\x40\x00\x00\x2c\x00\x01\x80\x0c\x00\x01\x00\x63\x6f\x75\x6e\x74\x65\x72\x00\x1c\x00\x02\x80\x0c\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x30\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x1c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x00\x10\x00\x02\x80\x0c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x00"),
- // batch end
- []byte("\x00\x00\x00\x0a"),
- }
- testConn := newTestConn(t, want)
- table := testConn.AddTable(&nftables.Table{
- Family: proto,
- Name: "ts-filter-test",
- })
- chain := testConn.AddChain(&nftables.Chain{
- Name: "ts-forward-test",
- Table: table,
- Type: nftables.ChainTypeFilter,
- Hooknum: nftables.ChainHookForward,
- Priority: nftables.ChainPriorityFilter,
- })
- err := addDropOutgoingPacketFromCGNATRangeRuleWithTunname(testConn, table, chain, "testTunn")
- if err != nil {
- t.Fatal(err)
- }
- }
- func TestAddAcceptOutgoingPacketRule(t *testing.T) {
- proto := nftables.TableFamilyIPv4
- want := [][]byte{
- // batch begin
- []byte("\x00\x00\x00\x0a"),
- // nft add table ip ts-filter-test
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x08\x00\x02\x00\x00\x00\x00\x00"),
- // nft add chain ip ts-filter-test ts-forward-test { type filter hook forward priority 0\; }
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x14\x00\x03\x00\x74\x73\x2d\x66\x6f\x72\x77\x61\x72\x64\x2d\x74\x65\x73\x74\x00\x14\x00\x04\x80\x08\x00\x01\x00\x00\x00\x00\x02\x08\x00\x02\x00\x00\x00\x00\x00\x0b\x00\x07\x00\x66\x69\x6c\x74\x65\x72\x00\x00"),
- // nft add rule ip ts-filter-test ts-forward-test oifname "testTunn" counter accept
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x14\x00\x02\x00\x74\x73\x2d\x66\x6f\x72\x77\x61\x72\x64\x2d\x74\x65\x73\x74\x00\xb4\x00\x04\x80\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x07\x08\x00\x01\x00\x00\x00\x00\x01\x30\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x24\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x10\x00\x03\x80\x0c\x00\x01\x00\x74\x65\x73\x74\x54\x75\x6e\x6e\x2c\x00\x01\x80\x0c\x00\x01\x00\x63\x6f\x75\x6e\x74\x65\x72\x00\x1c\x00\x02\x80\x0c\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x30\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x1c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x00\x10\x00\x02\x80\x0c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01"),
- // batch end
- []byte("\x00\x00\x00\x0a"),
- }
- testConn := newTestConn(t, want)
- table := testConn.AddTable(&nftables.Table{
- Family: proto,
- Name: "ts-filter-test",
- })
- chain := testConn.AddChain(&nftables.Chain{
- Name: "ts-forward-test",
- Table: table,
- Type: nftables.ChainTypeFilter,
- Hooknum: nftables.ChainHookForward,
- Priority: nftables.ChainPriorityFilter,
- })
- err := addAcceptOutgoingPacketRule(testConn, table, chain, "testTunn")
- if err != nil {
- t.Fatal(err)
- }
- }
- func TestAddAcceptIncomingPacketRule(t *testing.T) {
- proto := nftables.TableFamilyIPv4
- want := [][]byte{
- // batch begin
- []byte("\x00\x00\x00\x0a"),
- // nft add table ip ts-filter-test
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x08\x00\x02\x00\x00\x00\x00\x00"),
- // nft add chain ip ts-filter-test ts-input-test { type filter hook input priority 0\; }
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x12\x00\x03\x00\x74\x73\x2d\x69\x6e\x70\x75\x74\x2d\x74\x65\x73\x74\x00\x00\x00\x14\x00\x04\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0b\x00\x07\x00\x66\x69\x6c\x74\x65\x72\x00\x00"),
- // nft add rule ip ts-filter-test ts-input-test iifname "testTunn" counter accept
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x12\x00\x02\x00\x74\x73\x2d\x69\x6e\x70\x75\x74\x2d\x74\x65\x73\x74\x00\x00\x00\xb4\x00\x04\x80\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x06\x08\x00\x01\x00\x00\x00\x00\x01\x30\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x24\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x10\x00\x03\x80\x0c\x00\x01\x00\x74\x65\x73\x74\x54\x75\x6e\x6e\x2c\x00\x01\x80\x0c\x00\x01\x00\x63\x6f\x75\x6e\x74\x65\x72\x00\x1c\x00\x02\x80\x0c\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x30\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x1c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x00\x10\x00\x02\x80\x0c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01"),
- // batch end
- []byte("\x00\x00\x00\x0a"),
- }
- testConn := newTestConn(t, want)
- table := testConn.AddTable(&nftables.Table{
- Family: proto,
- Name: "ts-filter-test",
- })
- chain := testConn.AddChain(&nftables.Chain{
- Name: "ts-input-test",
- Table: table,
- Type: nftables.ChainTypeFilter,
- Hooknum: nftables.ChainHookInput,
- Priority: nftables.ChainPriorityFilter,
- })
- err := addAcceptIncomingPacketRule(testConn, table, chain, "testTunn")
- if err != nil {
- t.Fatal(err)
- }
- }
- func TestAddMatchSubnetRouteMarkRuleMasq(t *testing.T) {
- proto := nftables.TableFamilyIPv4
- want := [][]byte{
- // batch begin
- []byte("\x00\x00\x00\x0a"),
- // nft add table ip ts-nat-test
- []byte("\x02\x00\x00\x00\x10\x00\x01\x00\x74\x73\x2d\x6e\x61\x74\x2d\x74\x65\x73\x74\x00\x08\x00\x02\x00\x00\x00\x00\x00"),
- // nft add chain ip ts-nat-test ts-postrouting-test { type nat hook postrouting priority 100; }
- []byte("\x02\x00\x00\x00\x10\x00\x01\x00\x74\x73\x2d\x6e\x61\x74\x2d\x74\x65\x73\x74\x00\x18\x00\x03\x00\x74\x73\x2d\x70\x6f\x73\x74\x72\x6f\x75\x74\x69\x6e\x67\x2d\x74\x65\x73\x74\x00\x14\x00\x04\x80\x08\x00\x01\x00\x00\x00\x00\x04\x08\x00\x02\x00\x00\x00\x00\x64\x08\x00\x07\x00\x6e\x61\x74\x00"),
- // nft add rule ip ts-nat-test ts-postrouting-test meta mark & 0x00ff0000 == 0x00040000 counter masquerade
- []byte("\x02\x00\x00\x00\x10\x00\x01\x00\x74\x73\x2d\x6e\x61\x74\x2d\x74\x65\x73\x74\x00\x18\x00\x02\x00\x74\x73\x2d\x70\x6f\x73\x74\x72\x6f\x75\x74\x69\x6e\x67\x2d\x74\x65\x73\x74\x00\xf4\x00\x04\x80\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x03\x08\x00\x01\x00\x00\x00\x00\x01\x44\x00\x01\x80\x0c\x00\x01\x00\x62\x69\x74\x77\x69\x73\x65\x00\x34\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x01\x08\x00\x03\x00\x00\x00\x00\x04\x0c\x00\x04\x80\x08\x00\x01\x00\x00\xff\x00\x00\x0c\x00\x05\x80\x08\x00\x01\x00\x00\x00\x00\x00\x2c\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x20\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0c\x00\x03\x80\x08\x00\x01\x00\x00\x04\x00\x00\x2c\x00\x01\x80\x0c\x00\x01\x00\x63\x6f\x75\x6e\x74\x65\x72\x00\x1c\x00\x02\x80\x0c\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x30\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x1c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x00\x10\x00\x02\x80\x0c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01"),
- // batch end
- []byte("\x00\x00\x00\x0a"),
- }
- testConn := newTestConn(t, want)
- table := testConn.AddTable(&nftables.Table{
- Family: proto,
- Name: "ts-nat-test",
- })
- chain := testConn.AddChain(&nftables.Chain{
- Name: "ts-postrouting-test",
- Table: table,
- Type: nftables.ChainTypeNAT,
- Hooknum: nftables.ChainHookPostrouting,
- Priority: nftables.ChainPriorityNATSource,
- })
- err := addMatchSubnetRouteMarkRule(testConn, table, chain, Accept)
- if err != nil {
- t.Fatal(err)
- }
- }
- func TestAddMatchSubnetRouteMarkRuleAccept(t *testing.T) {
- proto := nftables.TableFamilyIPv4
- want := [][]byte{
- // batch begin
- []byte("\x00\x00\x00\x0a"),
- // nft add table ip ts-filter-test
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x08\x00\x02\x00\x00\x00\x00\x00"),
- // nft add chain ip ts-filter-test ts-forward-test { type filter hook forward priority 0\; }
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x14\x00\x03\x00\x74\x73\x2d\x66\x6f\x72\x77\x61\x72\x64\x2d\x74\x65\x73\x74\x00\x14\x00\x04\x80\x08\x00\x01\x00\x00\x00\x00\x02\x08\x00\x02\x00\x00\x00\x00\x00\x0b\x00\x07\x00\x66\x69\x6c\x74\x65\x72\x00\x00"),
- // nft add rule ip ts-filter-test ts-forward-test meta mark and 0x00ff0000 eq 0x00040000 counter accept
- []byte("\x02\x00\x00\x00\x13\x00\x01\x00\x74\x73\x2d\x66\x69\x6c\x74\x65\x72\x2d\x74\x65\x73\x74\x00\x00\x14\x00\x02\x00\x74\x73\x2d\x66\x6f\x72\x77\x61\x72\x64\x2d\x74\x65\x73\x74\x00\xf4\x00\x04\x80\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x03\x08\x00\x01\x00\x00\x00\x00\x01\x44\x00\x01\x80\x0c\x00\x01\x00\x62\x69\x74\x77\x69\x73\x65\x00\x34\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x01\x08\x00\x03\x00\x00\x00\x00\x04\x0c\x00\x04\x80\x08\x00\x01\x00\x00\xff\x00\x00\x0c\x00\x05\x80\x08\x00\x01\x00\x00\x00\x00\x00\x2c\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x20\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0c\x00\x03\x80\x08\x00\x01\x00\x00\x04\x00\x00\x2c\x00\x01\x80\x0c\x00\x01\x00\x63\x6f\x75\x6e\x74\x65\x72\x00\x1c\x00\x02\x80\x0c\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x30\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x1c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x00\x10\x00\x02\x80\x0c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01"),
- // batch end
- []byte("\x00\x00\x00\x0a"),
- }
- testConn := newTestConn(t, want)
- table := testConn.AddTable(&nftables.Table{
- Family: proto,
- Name: "ts-filter-test",
- })
- chain := testConn.AddChain(&nftables.Chain{
- Name: "ts-forward-test",
- Table: table,
- Type: nftables.ChainTypeFilter,
- Hooknum: nftables.ChainHookForward,
- Priority: nftables.ChainPriorityFilter,
- })
- err := addMatchSubnetRouteMarkRule(testConn, table, chain, Accept)
- if err != nil {
- t.Fatal(err)
- }
- }
- func newSysConn(t *testing.T) *nftables.Conn {
- t.Helper()
- if os.Geteuid() != 0 {
- t.Skip(t.Name(), " requires privileges to create a namespace in order to run")
- return nil
- }
- runtime.LockOSThread()
- ns, err := netns.New()
- if err != nil {
- t.Fatalf("netns.New() failed: %v", err)
- }
- c, err := nftables.New(nftables.WithNetNSFd(int(ns)))
- if err != nil {
- t.Fatalf("nftables.New() failed: %v", err)
- }
- t.Cleanup(func() { cleanupSysConn(t, ns) })
- return c
- }
- func cleanupSysConn(t *testing.T, ns netns.NsHandle) {
- defer runtime.UnlockOSThread()
- if err := ns.Close(); err != nil {
- t.Fatalf("newNS.Close() failed: %v", err)
- }
- }
- func checkChains(t *testing.T, conn *nftables.Conn, fam nftables.TableFamily, wantCount int) {
- t.Helper()
- got, err := conn.ListChainsOfTableFamily(fam)
- if err != nil {
- t.Fatalf("conn.ListChainsOfTableFamily(%v) failed: %v", fam, err)
- }
- if len(got) != wantCount {
- t.Fatalf("len(got) = %d, want %d", len(got), wantCount)
- }
- }
- func checkTables(t *testing.T, conn *nftables.Conn, fam nftables.TableFamily, wantCount int) {
- t.Helper()
- got, err := conn.ListTablesOfFamily(fam)
- if err != nil {
- t.Fatalf("conn.ListTablesOfFamily(%v) failed: %v", fam, err)
- }
- if len(got) != wantCount {
- t.Fatalf("len(got) = %d, want %d", len(got), wantCount)
- }
- }
- func TestAddAndDelNetfilterChains(t *testing.T) {
- type test struct {
- hostHasIPv6 bool
- initIPv4ChainCount int
- initIPv6ChainCount int
- ipv4TableCount int
- ipv6TableCount int
- ipv4ChainCount int
- ipv6ChainCount int
- ipv4ChainCountPostDelete int
- ipv6ChainCountPostDelete int
- }
- tests := []test{
- {
- hostHasIPv6: true,
- initIPv4ChainCount: 0,
- initIPv6ChainCount: 0,
- ipv4TableCount: 2,
- ipv6TableCount: 2,
- ipv4ChainCount: 6,
- ipv6ChainCount: 6,
- ipv4ChainCountPostDelete: 3,
- ipv6ChainCountPostDelete: 3,
- },
- { // host without IPv6 support
- ipv4TableCount: 2,
- ipv4ChainCount: 6,
- ipv4ChainCountPostDelete: 3,
- }}
- for _, tt := range tests {
- t.Logf("running a test case for IPv6 support: %v", tt.hostHasIPv6)
- conn := newSysConn(t)
- runner := newFakeNftablesRunnerWithConn(t, conn, tt.hostHasIPv6)
- // Check that we start off with no chains.
- checkChains(t, conn, nftables.TableFamilyIPv4, tt.initIPv4ChainCount)
- checkChains(t, conn, nftables.TableFamilyIPv6, tt.initIPv6ChainCount)
- if err := runner.AddChains(); err != nil {
- t.Fatalf("runner.AddChains() failed: %v", err)
- }
- // Check that the amount of tables for each IP family is as expected.
- checkTables(t, conn, nftables.TableFamilyIPv4, tt.ipv4TableCount)
- checkTables(t, conn, nftables.TableFamilyIPv6, tt.ipv6TableCount)
- // Check that the amount of chains for each IP family is as expected.
- checkChains(t, conn, nftables.TableFamilyIPv4, tt.ipv4ChainCount)
- checkChains(t, conn, nftables.TableFamilyIPv6, tt.ipv6ChainCount)
- if err := runner.DelChains(); err != nil {
- t.Fatalf("runner.DelChains() failed: %v", err)
- }
- // Test that the tables as well as the default chains are still present.
- checkChains(t, conn, nftables.TableFamilyIPv4, tt.ipv4ChainCountPostDelete)
- checkChains(t, conn, nftables.TableFamilyIPv6, tt.ipv6ChainCountPostDelete)
- checkTables(t, conn, nftables.TableFamilyIPv4, tt.ipv4TableCount)
- checkTables(t, conn, nftables.TableFamilyIPv6, tt.ipv6TableCount)
- }
- }
- func getTsChains(
- conn *nftables.Conn,
- proto nftables.TableFamily) (*nftables.Chain, *nftables.Chain, *nftables.Chain, error) {
- chains, err := conn.ListChainsOfTableFamily(nftables.TableFamilyIPv4)
- if err != nil {
- return nil, nil, nil, fmt.Errorf("list chains failed: %w", err)
- }
- var chainInput, chainForward, chainPostrouting *nftables.Chain
- for _, chain := range chains {
- switch chain.Name {
- case "ts-input":
- chainInput = chain
- case "ts-forward":
- chainForward = chain
- case "ts-postrouting":
- chainPostrouting = chain
- }
- }
- return chainInput, chainForward, chainPostrouting, nil
- }
- // findV4BaseRules verifies that the base rules are present in the input and forward chains.
- func findV4BaseRules(
- conn *nftables.Conn,
- inpChain *nftables.Chain,
- forwChain *nftables.Chain,
- tunname string) ([]*nftables.Rule, error) {
- want := []*nftables.Rule{}
- rule, err := createRangeRule(inpChain.Table, inpChain, tunname, tsaddr.ChromeOSVMRange(), expr.VerdictReturn)
- if err != nil {
- return nil, fmt.Errorf("create rule: %w", err)
- }
- want = append(want, rule)
- rule, err = createRangeRule(inpChain.Table, inpChain, tunname, tsaddr.CGNATRange(), expr.VerdictDrop)
- if err != nil {
- return nil, fmt.Errorf("create rule: %w", err)
- }
- want = append(want, rule)
- rule, err = createDropOutgoingPacketFromCGNATRangeRuleWithTunname(forwChain.Table, forwChain, tunname)
- if err != nil {
- return nil, fmt.Errorf("create rule: %w", err)
- }
- want = append(want, rule)
- get := []*nftables.Rule{}
- for _, rule := range want {
- getRule, err := findRule(conn, rule)
- if err != nil {
- return nil, fmt.Errorf("find rule: %w", err)
- }
- get = append(get, getRule)
- }
- return get, nil
- }
- func findCommonBaseRules(
- conn *nftables.Conn,
- forwChain *nftables.Chain,
- tunname string) ([]*nftables.Rule, error) {
- want := []*nftables.Rule{}
- rule, err := createSetSubnetRouteMarkRule(forwChain.Table, forwChain, tunname)
- if err != nil {
- return nil, fmt.Errorf("create rule: %w", err)
- }
- want = append(want, rule)
- rule, err = createMatchSubnetRouteMarkRule(forwChain.Table, forwChain, Accept)
- if err != nil {
- return nil, fmt.Errorf("create rule: %w", err)
- }
- want = append(want, rule)
- rule = createAcceptOutgoingPacketRule(forwChain.Table, forwChain, tunname)
- want = append(want, rule)
- get := []*nftables.Rule{}
- for _, rule := range want {
- getRule, err := findRule(conn, rule)
- if err != nil {
- return nil, fmt.Errorf("find rule: %w", err)
- }
- get = append(get, getRule)
- }
- return get, nil
- }
- // checkChainRules verifies that the chain has the expected number of rules.
- func checkChainRules(t *testing.T, conn *nftables.Conn, chain *nftables.Chain, wantCount int) {
- t.Helper()
- got, err := conn.GetRules(chain.Table, chain)
- if err != nil {
- t.Fatalf("conn.GetRules() failed: %v", err)
- }
- if len(got) != wantCount {
- t.Fatalf("got = %d, want %d", len(got), wantCount)
- }
- }
- func TestNFTAddAndDelNetfilterBase(t *testing.T) {
- conn := newSysConn(t)
- runner := newFakeNftablesRunnerWithConn(t, conn, true)
- if err := runner.AddChains(); err != nil {
- t.Fatalf("AddChains() failed: %v", err)
- }
- defer runner.DelChains()
- if err := runner.AddBase("testTunn"); err != nil {
- t.Fatalf("AddBase() failed: %v", err)
- }
- // check number of rules in each IPv4 TS chain
- inputV4, forwardV4, postroutingV4, err := getTsChains(conn, nftables.TableFamilyIPv4)
- if err != nil {
- t.Fatalf("getTsChains() failed: %v", err)
- }
- checkChainRules(t, conn, inputV4, 3)
- checkChainRules(t, conn, forwardV4, 4)
- checkChainRules(t, conn, postroutingV4, 0)
- _, err = findV4BaseRules(conn, inputV4, forwardV4, "testTunn")
- if err != nil {
- t.Fatalf("missing v4 base rule: %v", err)
- }
- _, err = findCommonBaseRules(conn, forwardV4, "testTunn")
- if err != nil {
- t.Fatalf("missing v4 base rule: %v", err)
- }
- // Check number of rules in each IPv6 TS chain.
- inputV6, forwardV6, postroutingV6, err := getTsChains(conn, nftables.TableFamilyIPv6)
- if err != nil {
- t.Fatalf("getTsChains() failed: %v", err)
- }
- checkChainRules(t, conn, inputV6, 3)
- checkChainRules(t, conn, forwardV6, 4)
- checkChainRules(t, conn, postroutingV6, 0)
- _, err = findCommonBaseRules(conn, forwardV6, "testTunn")
- if err != nil {
- t.Fatalf("missing v6 base rule: %v", err)
- }
- runner.DelBase()
- chains, err := conn.ListChains()
- if err != nil {
- t.Fatalf("conn.ListChains() failed: %v", err)
- }
- for _, chain := range chains {
- checkChainRules(t, conn, chain, 0)
- }
- }
- func findLoopBackRule(conn *nftables.Conn, proto nftables.TableFamily, table *nftables.Table, chain *nftables.Chain, addr netip.Addr) (*nftables.Rule, error) {
- matchingAddr := addr.AsSlice()
- saddrExpr, err := newLoadSaddrExpr(proto, 1)
- if err != nil {
- return nil, fmt.Errorf("get expr: %w", err)
- }
- loopBackRule := &nftables.Rule{
- Table: table,
- Chain: chain,
- Exprs: []expr.Any{
- &expr.Meta{
- Key: expr.MetaKeyIIFNAME,
- Register: 1,
- },
- &expr.Cmp{
- Op: expr.CmpOpEq,
- Register: 1,
- Data: []byte("lo"),
- },
- saddrExpr,
- &expr.Cmp{
- Op: expr.CmpOpEq,
- Register: 1,
- Data: matchingAddr,
- },
- &expr.Counter{},
- &expr.Verdict{
- Kind: expr.VerdictAccept,
- },
- },
- }
- existingLoopBackRule, err := findRule(conn, loopBackRule)
- if err != nil {
- return nil, fmt.Errorf("find loop back rule: %w", err)
- }
- return existingLoopBackRule, nil
- }
- func TestNFTAddAndDelLoopbackRule(t *testing.T) {
- conn := newSysConn(t)
- runner := newFakeNftablesRunnerWithConn(t, conn, true)
- if err := runner.AddChains(); err != nil {
- t.Fatalf("AddChains() failed: %v", err)
- }
- defer runner.DelChains()
- inputV4, _, _, err := getTsChains(conn, nftables.TableFamilyIPv4)
- if err != nil {
- t.Fatalf("getTsChains() failed: %v", err)
- }
- inputV6, _, _, err := getTsChains(conn, nftables.TableFamilyIPv6)
- if err != nil {
- t.Fatalf("getTsChains() failed: %v", err)
- }
- checkChainRules(t, conn, inputV4, 0)
- checkChainRules(t, conn, inputV6, 0)
- runner.AddBase("testTunn")
- defer runner.DelBase()
- checkChainRules(t, conn, inputV4, 3)
- checkChainRules(t, conn, inputV6, 3)
- addr := netip.MustParseAddr("192.168.0.2")
- addrV6 := netip.MustParseAddr("2001:db8::2")
- runner.AddLoopbackRule(addr)
- runner.AddLoopbackRule(addrV6)
- checkChainRules(t, conn, inputV4, 4)
- checkChainRules(t, conn, inputV6, 4)
- existingLoopBackRule, err := findLoopBackRule(conn, nftables.TableFamilyIPv4, runner.nft4.Filter, inputV4, addr)
- if err != nil {
- t.Fatalf("findLoopBackRule() failed: %v", err)
- }
- if existingLoopBackRule.Position != 0 {
- t.Fatalf("existingLoopBackRule.Handle = %d, want 0", existingLoopBackRule.Handle)
- }
- existingLoopBackRuleV6, err := findLoopBackRule(conn, nftables.TableFamilyIPv6, runner.nft6.Filter, inputV6, addrV6)
- if err != nil {
- t.Fatalf("findLoopBackRule() failed: %v", err)
- }
- if existingLoopBackRuleV6.Position != 0 {
- t.Fatalf("existingLoopBackRule.Handle = %d, want 0", existingLoopBackRule.Handle)
- }
- runner.DelLoopbackRule(addr)
- runner.DelLoopbackRule(addrV6)
- checkChainRules(t, conn, inputV4, 3)
- checkChainRules(t, conn, inputV6, 3)
- }
- func TestNFTAddAndDelHookRule(t *testing.T) {
- conn := newSysConn(t)
- runner := newFakeNftablesRunnerWithConn(t, conn, true)
- if err := runner.AddChains(); err != nil {
- t.Fatalf("AddChains() failed: %v", err)
- }
- defer runner.DelChains()
- if err := runner.AddHooks(); err != nil {
- t.Fatalf("AddHooks() failed: %v", err)
- }
- forwardChain, err := getChainFromTable(conn, runner.nft4.Filter, "FORWARD")
- if err != nil {
- t.Fatalf("failed to get forwardChain: %v", err)
- }
- inputChain, err := getChainFromTable(conn, runner.nft4.Filter, "INPUT")
- if err != nil {
- t.Fatalf("failed to get inputChain: %v", err)
- }
- postroutingChain, err := getChainFromTable(conn, runner.nft4.Nat, "POSTROUTING")
- if err != nil {
- t.Fatalf("failed to get postroutingChain: %v", err)
- }
- checkChainRules(t, conn, forwardChain, 1)
- checkChainRules(t, conn, inputChain, 1)
- checkChainRules(t, conn, postroutingChain, 1)
- runner.DelHooks(t.Logf)
- checkChainRules(t, conn, forwardChain, 0)
- checkChainRules(t, conn, inputChain, 0)
- checkChainRules(t, conn, postroutingChain, 0)
- }
- type testFWDetector struct {
- iptRuleCount, nftRuleCount int
- iptErr, nftErr error
- }
- func (t *testFWDetector) iptDetect() (int, error) {
- return t.iptRuleCount, t.iptErr
- }
- func (t *testFWDetector) nftDetect() (int, error) {
- return t.nftRuleCount, t.nftErr
- }
- // TestCreateDummyPostroutingChains tests that on a system with nftables
- // available, the function does not return an error and that the dummy
- // postrouting chains are cleaned up.
- func TestCreateDummyPostroutingChains(t *testing.T) {
- conn := newSysConn(t)
- runner := newFakeNftablesRunnerWithConn(t, conn, true)
- if err := runner.createDummyPostroutingChains(); err != nil {
- t.Fatalf("createDummyPostroutingChains() failed: %v", err)
- }
- for _, table := range runner.getTables() {
- nt, err := getTableIfExists(conn, table.Proto, tsDummyTableName)
- if err != nil {
- t.Fatalf("getTableIfExists() failed: %v", err)
- }
- if nt != nil {
- t.Fatalf("expected table to be nil, got %v", nt)
- }
- }
- }
- func TestPickFirewallModeFromInstalledRules(t *testing.T) {
- tests := []struct {
- name string
- det *testFWDetector
- want FirewallMode
- }{
- {
- name: "using iptables legacy",
- det: &testFWDetector{iptRuleCount: 1},
- want: FirewallModeIPTables,
- },
- {
- name: "using nftables",
- det: &testFWDetector{nftRuleCount: 1},
- want: FirewallModeNfTables,
- },
- {
- name: "using both iptables and nftables",
- det: &testFWDetector{iptRuleCount: 2, nftRuleCount: 2},
- want: FirewallModeNfTables,
- },
- {
- name: "not using any firewall, both available",
- det: &testFWDetector{},
- want: FirewallModeNfTables,
- },
- {
- name: "not using any firewall, iptables available only",
- det: &testFWDetector{iptRuleCount: 1, nftErr: errors.New("nft error")},
- want: FirewallModeIPTables,
- },
- {
- name: "not using any firewall, nftables available only",
- det: &testFWDetector{iptErr: errors.New("iptables error"), nftRuleCount: 1},
- want: FirewallModeNfTables,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got := pickFirewallModeFromInstalledRules(t.Logf, tt.det)
- if got != tt.want {
- t.Errorf("chooseFireWallMode() = %v, want %v", got, tt.want)
- }
- })
- }
- }
- // This test creates a temporary network namespace for the nftables rules being
- // set up, so it needs to run in a privileged mode. Locally it needs to be run
- // by root, else it will be silently skipped. In CI it runs in a privileged
- // container.
- func TestEnsureSNATForDst_nftables(t *testing.T) {
- conn := newSysConn(t)
- runner := newFakeNftablesRunnerWithConn(t, conn, true)
- ip1, ip2, ip3 := netip.MustParseAddr("100.99.99.99"), netip.MustParseAddr("100.88.88.88"), netip.MustParseAddr("100.77.77.77")
- // 1. A new rule gets added
- mustCreateSNATRule_nft(t, runner, ip1, ip2)
- chainRuleCount(t, "POSTROUTING", 1, conn, nftables.TableFamilyIPv4)
- checkSNATRule_nft(t, runner, runner.nft4.Proto, ip1, ip2)
- // 2. Another call to EnsureSNATForDst with the same src and dst does not result in another rule being added.
- mustCreateSNATRule_nft(t, runner, ip1, ip2)
- chainRuleCount(t, "POSTROUTING", 1, conn, nftables.TableFamilyIPv4) // still just one rule
- checkSNATRule_nft(t, runner, runner.nft4.Proto, ip1, ip2)
- // 3. Another call to EnsureSNATForDst with a different src and the same dst results in the earlier rule being
- // deleted.
- mustCreateSNATRule_nft(t, runner, ip3, ip2)
- chainRuleCount(t, "POSTROUTING", 1, conn, nftables.TableFamilyIPv4) // still just one rule
- checkSNATRule_nft(t, runner, runner.nft4.Proto, ip3, ip2)
- // 4. Another call to EnsureSNATForDst with a different dst should not get the earlier rule deleted.
- mustCreateSNATRule_nft(t, runner, ip3, ip1)
- chainRuleCount(t, "POSTROUTING", 2, conn, nftables.TableFamilyIPv4) // now two rules
- checkSNATRule_nft(t, runner, runner.nft4.Proto, ip3, ip1)
- }
- func newFakeNftablesRunnerWithConn(t *testing.T, conn *nftables.Conn, hasIPv6 bool) *nftablesRunner {
- t.Helper()
- if !hasIPv6 {
- tstest.Replace(t, &checkIPv6ForTest, func(logger.Logf) error {
- return errors.New("test: no IPv6")
- })
- }
- return newNfTablesRunnerWithConn(t.Logf, conn)
- }
- func mustCreateSNATRule_nft(t *testing.T, runner *nftablesRunner, src, dst netip.Addr) {
- t.Helper()
- if err := runner.EnsureSNATForDst(src, dst); err != nil {
- t.Fatalf("error ensuring SNAT rule: %v", err)
- }
- }
- // checkSNATRule_nft verifies that a SNAT rule for the given destination and source exists.
- func checkSNATRule_nft(t *testing.T, runner *nftablesRunner, fam nftables.TableFamily, src, dst netip.Addr) {
- t.Helper()
- chains, err := runner.conn.ListChainsOfTableFamily(fam)
- if err != nil {
- t.Fatalf("error listing chains: %v", err)
- }
- var chain *nftables.Chain
- for _, ch := range chains {
- if ch.Name == "POSTROUTING" {
- chain = ch
- break
- }
- }
- if chain == nil {
- t.Fatal("POSTROUTING chain does not exist")
- }
- meta := []byte(fmt.Sprintf("dst:%s,src:%s", dst.String(), src.String()))
- wantsRule := snatRule(chain.Table, chain, src, dst, meta)
- checkRule(t, wantsRule, runner.conn)
- }
|