From c463c56f86bc52c96a6ed3d1827583192a31a245 Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Thu, 6 Aug 2026 20:19:10 +0000 Subject: [PATCH] moare fixes --- internal/firewall/nftables/input.go | 2 +- internal/firewall/nftables/output.go | 138 +++++++++++++++++++-- internal/firewall/nftables/redirect.go | 4 +- internal/firewall/nftables/support.go | 2 +- internal/firewall/nftables/tcp.go | 12 +- internal/pmtud/pmtud_integration_test.go | 3 +- internal/pmtud/tcp/helpers_test.go | 3 +- internal/pmtud/tcp/tcp_integration_test.go | 3 +- 8 files changed, 145 insertions(+), 22 deletions(-) diff --git a/internal/firewall/nftables/input.go b/internal/firewall/nftables/input.go index 3f74fa2a..6e419925 100644 --- a/internal/firewall/nftables/input.go +++ b/internal/firewall/nftables/input.go @@ -63,7 +63,7 @@ func (f *Firewall) AcceptInputToPort(_ context.Context, intf string, port uint16 } table, inputChain, _, _ := setupFilterWithBaseChains(conn) - portBytes := []byte{byte(port >> 8), byte(port)} //nolint:mnd + portBytes := []byte{byte(port >> 8), byte(port)} //nolint:mnd,gosec // network byte order const tcp, udp uint8 = 6, 17 protocols := []uint8{tcp, udp} diff --git a/internal/firewall/nftables/output.go b/internal/firewall/nftables/output.go index 2fa9c24c..15283f60 100644 --- a/internal/firewall/nftables/output.go +++ b/internal/firewall/nftables/output.go @@ -215,6 +215,126 @@ func (f *Firewall) AcceptOutputTrafficToVPN(_ context.Context, defaultInterface return nil } +func (f *Firewall) AcceptOutput(_ context.Context, protocol, intf string, + ip netip.Addr, port uint16, remove bool, +) error { + f.mutex.Lock() + defer f.mutex.Unlock() + + conn, err := nftables.New() + if err != nil { + return fmt.Errorf("creating nftables connection: %w", err) + } + + table, _, _, outputChain := setupFilterWithBaseChains(conn) + + const maxExprsLen = 7 + exprs := make([]expr.Any, 0, maxExprsLen) + + if intf != "" && intf != "*" { + exprs = append(exprs, + &expr.Meta{Key: expr.MetaKeyOIFNAME, Register: 1}, + &expr.Cmp{Op: expr.CmpOpEq, Register: 1, Data: []byte(intf + "\x00")}, + ) + } + + if ip.Is4() { + exprs = append(exprs, + &expr.Payload{ + DestRegister: 1, + Base: expr.PayloadBaseNetworkHeader, + Offset: 16, //nolint:mnd + Len: 4, //nolint:mnd + }, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: ip.AsSlice(), + }, + ) + } else { + exprs = append(exprs, + &expr.Payload{ + DestRegister: 1, + Base: expr.PayloadBaseNetworkHeader, + Offset: 24, //nolint:mnd + Len: 16, //nolint:mnd + }, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: ip.AsSlice(), + }, + ) + } + + var protocolByte uint8 + switch protocol { + case "tcp": + protocolByte = 6 + case "udp": + protocolByte = 17 + default: + return fmt.Errorf("unsupported protocol: %s", protocol) + } + + exprs = append(exprs, + &expr.Payload{ + DestRegister: 1, + Base: expr.PayloadBaseTransportHeader, + Offset: 3, //nolint:mnd + Len: 1, + }, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte{protocolByte}, + }, + ) + + portBytes := []byte{byte(port >> 8), byte(port)} //nolint:mnd,gosec + exprs = append(exprs, + &expr.Payload{ + DestRegister: 1, + Base: expr.PayloadBaseTransportHeader, + Offset: 2, //nolint:mnd + Len: 2, //nolint:mnd + }, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: portBytes, + }, + &expr.Verdict{Kind: expr.VerdictAccept}, + ) + + rule := &nftables.Rule{ + Table: table, + Chain: outputChain, + Exprs: exprs, + } + + if !remove { + conn.AddRule(rule) + f.rules = append(f.rules, rule) + } else { + err = f.deleteRule(conn, rule) + if err != nil { + return fmt.Errorf("deleting rule: %w", err) + } + } + + err = conn.Flush() + if err != nil { + if !remove { + f.rules = f.rules[:len(f.rules)-1] + } + return fmt.Errorf("flushing: %w", err) + } + + return nil +} + func (f *Firewall) AcceptOutputFromIPPortToIPPort(_ context.Context, protocol, intf string, source, destination netip.AddrPort, remove bool, ) error { @@ -228,7 +348,7 @@ func (f *Firewall) AcceptOutputFromIPPortToIPPort(_ context.Context, protocol, i table, _, _, outputChain := setupFilterWithBaseChains(conn) - const maxExprsLen = 10 //nolint:mnd + const maxExprsLen = 10 exprs := make([]expr.Any, 0, maxExprsLen) if intf != "" && intf != "*" { @@ -301,11 +421,11 @@ func (f *Firewall) AcceptOutputFromIPPortToIPPort(_ context.Context, protocol, i var protocolByte uint8 switch protocol { case "tcp": - protocolByte = 6 //nolint:mnd + protocolByte = 6 case "udp": - protocolByte = 17 //nolint:mnd + protocolByte = 17 default: - return fmt.Errorf("unsupported protocol: %s", protocol) //nolint:err113 + return fmt.Errorf("unsupported protocol: %s", protocol) } exprs = append(exprs, @@ -317,13 +437,13 @@ func (f *Firewall) AcceptOutputFromIPPortToIPPort(_ context.Context, protocol, i }, ) - sourcePortBytes := []byte{byte(source.Port() >> 8), byte(source.Port())} //nolint:mnd - destinationPortBytes := []byte{byte(destination.Port() >> 8), byte(destination.Port())} //nolint:mnd + sourcePortBytes := []byte{byte(source.Port() >> 8), byte(source.Port())} //nolint:mnd,gosec + destinationPortBytes := []byte{byte(destination.Port() >> 8), byte(destination.Port())} //nolint:mnd,gosec exprs = append(exprs, &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseTransportHeader, - Offset: 0, //nolint:mnd + Offset: 0, Len: 2, //nolint:mnd }, &expr.Cmp{ @@ -440,7 +560,7 @@ func (f *Firewall) AcceptOutputFromIPToSubnet(_ context.Context, intf string, as DestRegister: 1, Len: 4, //nolint:mnd Mask: mask, - Xor: []byte{0, 0, 0, 0}, //nolint:mnd + Xor: []byte{0, 0, 0, 0}, }, &expr.Cmp{ Op: expr.CmpOpEq, @@ -463,7 +583,7 @@ func (f *Firewall) AcceptOutputFromIPToSubnet(_ context.Context, intf string, as DestRegister: 1, Len: 16, //nolint:mnd Mask: mask, - Xor: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //nolint:mnd + Xor: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }, &expr.Cmp{ Op: expr.CmpOpEq, diff --git a/internal/firewall/nftables/redirect.go b/internal/firewall/nftables/redirect.go index cd79fdeb..f1be347d 100644 --- a/internal/firewall/nftables/redirect.go +++ b/internal/firewall/nftables/redirect.go @@ -42,8 +42,8 @@ func (f *Firewall) RedirectPort(_ context.Context, intf string, Priority: nftables.ChainPriorityNATDest, }) - sourcePortBytes := []byte{byte(sourcePort >> 8), byte(sourcePort)} //nolint:mnd - destinationPortBytes := []byte{byte(destinationPort >> 8), byte(destinationPort)} //nolint:mnd + sourcePortBytes := []byte{byte(sourcePort >> 8), byte(sourcePort)} //nolint:mnd,gosec + destinationPortBytes := []byte{byte(destinationPort >> 8), byte(destinationPort)} //nolint:mnd,gosec const tcp, udp uint8 = 6, 17 //nolint:mnd protocols := []uint8{tcp, udp} diff --git a/internal/firewall/nftables/support.go b/internal/firewall/nftables/support.go index ac80616b..0cba961d 100644 --- a/internal/firewall/nftables/support.go +++ b/internal/firewall/nftables/support.go @@ -33,7 +33,7 @@ func (f *Firewall) Version(ctx context.Context) (string, error) { outputStr := strings.TrimSpace(string(output)) words := strings.Fields(outputStr) if len(words) == 0 { - return "", errors.New(emptyVersionError) //nolint:err113 + return "", errors.New(emptyVersionError) } return words[0], nil } diff --git a/internal/firewall/nftables/tcp.go b/internal/firewall/nftables/tcp.go index c52de68a..5a439a46 100644 --- a/internal/firewall/nftables/tcp.go +++ b/internal/firewall/nftables/tcp.go @@ -94,16 +94,16 @@ func (f *Firewall) TempDropOutputTCPRST(_ context.Context, // Match TCP protocol (6) exprs = append(exprs, &expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1}, - &expr.Cmp{Op: expr.CmpOpEq, Register: 1, Data: []byte{6}}, //nolint:mnd + &expr.Cmp{Op: expr.CmpOpEq, Register: 1, Data: []byte{6}}, ) // Match source port - srcPortBytes := []byte{byte(src.Port() >> 8), byte(src.Port())} //nolint:mnd + srcPortBytes := []byte{byte(src.Port() >> 8), byte(src.Port())} //nolint:mnd,gosec // network byte order exprs = append(exprs, &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseTransportHeader, - Offset: 0, //nolint:mnd + Offset: 0, Len: 2, //nolint:mnd }, &expr.Cmp{ @@ -114,7 +114,7 @@ func (f *Firewall) TempDropOutputTCPRST(_ context.Context, ) // Match destination port - dstPortBytes := []byte{byte(dst.Port() >> 8), byte(dst.Port())} //nolint:mnd + dstPortBytes := []byte{byte(dst.Port() >> 8), byte(dst.Port())} //nolint:mnd,gosec // network byte order exprs = append(exprs, &expr.Payload{ DestRegister: 1, @@ -143,12 +143,12 @@ func (f *Firewall) TempDropOutputTCPRST(_ context.Context, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, - Data: []byte{0x04}, //nolint:mnd + Data: []byte{0x04}, }, ) // Exclude packets with the mark using mark != excludeMark - markData := []byte{ + markData := []byte{ //nolint:gosec // mark is int (32-bit), byte conversions are intentional byte(excludeMark), byte(excludeMark >> 8), byte(excludeMark >> 16), byte(excludeMark >> 24), //nolint:mnd } exprs = append(exprs, diff --git a/internal/pmtud/pmtud_integration_test.go b/internal/pmtud/pmtud_integration_test.go index 54958a6a..1e9a9f06 100644 --- a/internal/pmtud/pmtud_integration_test.go +++ b/internal/pmtud/pmtud_integration_test.go @@ -23,7 +23,8 @@ func Test_PathMTUDiscover(t *testing.T) { logger := log.New(log.SetLevel(log.LevelDebug)) cmder := command.New() - fw, err := firewall.NewConfig(t.Context(), logger, logger, cmder, nil, nil) + const implementation = "auto" + fw, err := firewall.NewConfig(t.Context(), implementation, logger, logger, cmder, nil, nil) if errors.Is(err, iptables.ErrNotSupported) { t.Skip("iptables not installed, skipping TCP PMTUD tests") } diff --git a/internal/pmtud/tcp/helpers_test.go b/internal/pmtud/tcp/helpers_test.go index f97bf419..ad8bd036 100644 --- a/internal/pmtud/tcp/helpers_test.go +++ b/internal/pmtud/tcp/helpers_test.go @@ -35,7 +35,8 @@ func getFirewall(t *testing.T) *firewall.Config { noopLogger := &noopLogger{} cmder := command.New() var err error - testFirewall, err = firewall.NewConfig(t.Context(), noopLogger, noopLogger, cmder, nil, nil) + const implementation = "auto" + testFirewall, err = firewall.NewConfig(t.Context(), implementation, noopLogger, noopLogger, cmder, nil, nil) if errors.Is(err, iptables.ErrNotSupported) { t.Skip("iptables not installed, skipping TCP PMTUD tests") } diff --git a/internal/pmtud/tcp/tcp_integration_test.go b/internal/pmtud/tcp/tcp_integration_test.go index e31a45b4..043f8372 100644 --- a/internal/pmtud/tcp/tcp_integration_test.go +++ b/internal/pmtud/tcp/tcp_integration_test.go @@ -33,7 +33,8 @@ func Test_PathMTUDiscover(t *testing.T) { logger := log.New(log.SetLevel(log.LevelDebug)) cmder := command.New() - fw, err := firewall.NewConfig(t.Context(), logger, logger, cmder, nil, nil) + const implementation = "auto" + fw, err := firewall.NewConfig(t.Context(), implementation, logger, logger, cmder, nil, nil) if errors.Is(err, iptables.ErrNotSupported) { t.Skip("iptables not installed, skipping TCP PMTUD tests") }