diff --git a/internal/firewall/firewall.go b/internal/firewall/firewall.go index cb9ac873..6f380d22 100644 --- a/internal/firewall/firewall.go +++ b/internal/firewall/firewall.go @@ -42,6 +42,9 @@ type configurator struct { //nolint:maligned localIP net.IP networkInfoMutex sync.Mutex + // Fixed state + ip6Tables bool + // State enabled bool vpnConnection models.OpenVPNConnection @@ -52,12 +55,14 @@ type configurator struct { //nolint:maligned // NewConfigurator creates a new Configurator instance. func NewConfigurator(logger logging.Logger, routing routing.Routing, openFile os.OpenFileFunc) Configurator { + commander := command.NewCommander() return &configurator{ - commander: command.NewCommander(), + commander: commander, logger: logger.NewChild(logging.SetPrefix("firewall: ")), routing: routing, openFile: openFile, allowedInputPorts: make(map[uint16]string), + ip6Tables: ip6tablesSupported(context.Background(), commander), } } diff --git a/internal/firewall/ip6tables.go b/internal/firewall/ip6tables.go index 9666badd..01c0a42c 100644 --- a/internal/firewall/ip6tables.go +++ b/internal/firewall/ip6tables.go @@ -5,12 +5,22 @@ import ( "errors" "fmt" "strings" + + "github.com/qdm12/golibs/command" ) var ( - ErrIP6Tables = errors.New("failed ip6tables command") + ErrIP6Tables = errors.New("failed ip6tables command") + ErrIP6NotSupported = errors.New("ip6tables not supported") ) +func ip6tablesSupported(ctx context.Context, commander command.Commander) (supported bool) { + if _, err := commander.Run(ctx, "ip6tables", "-L"); err != nil { + return false + } + return true +} + func (c *configurator) runIP6tablesInstructions(ctx context.Context, instructions []string) error { for _, instruction := range instructions { if err := c.runIP6tablesInstruction(ctx, instruction); err != nil { @@ -21,6 +31,9 @@ func (c *configurator) runIP6tablesInstructions(ctx context.Context, instruction } func (c *configurator) runIP6tablesInstruction(ctx context.Context, instruction string) error { + if !c.ip6Tables { + return nil + } c.ip6tablesMutex.Lock() // only one ip6tables command at once defer c.ip6tablesMutex.Unlock() if c.debug { diff --git a/internal/firewall/iptables.go b/internal/firewall/iptables.go index 4b9d6574..fccbacba 100644 --- a/internal/firewall/iptables.go +++ b/internal/firewall/iptables.go @@ -18,6 +18,7 @@ var ( ErrPolicyUnknown = errors.New("unknown policy") ErrClearRules = errors.New("cannot clear all rules") ErrSetIPtablesPolicies = errors.New("cannot set iptables policies") + ErrNeedIP6Tables = errors.New("ip6tables is required, please upgrade your kernel to support it") ) func appendOrDelete(remove bool) string { @@ -125,6 +126,9 @@ func (c *configurator) acceptInputToSubnet(ctx context.Context, intf string, des if isIP4Subnet { return c.runIptablesInstruction(ctx, instruction) } + if !c.ip6Tables { + return fmt.Errorf("accept input to subnet %s: %w", destination, ErrNeedIP6Tables) + } return c.runIP6tablesInstruction(ctx, instruction) } @@ -149,6 +153,8 @@ func (c *configurator) acceptOutputTrafficToVPN(ctx context.Context, isIPv4 := connection.IP.To4() != nil if isIPv4 { return c.runIptablesInstruction(ctx, instruction) + } else if !c.ip6Tables { + return fmt.Errorf("accept output to VPN server: %w", ErrNeedIP6Tables) } return c.runIP6tablesInstruction(ctx, instruction) } @@ -168,6 +174,8 @@ func (c *configurator) acceptOutputFromIPToSubnet(ctx context.Context, if doIPv4 { return c.runIptablesInstruction(ctx, instruction) + } else if !c.ip6Tables { + return fmt.Errorf("accept output from %s to %s: %w", sourceIP, destinationSubnet, ErrNeedIP6Tables) } return c.runIP6tablesInstruction(ctx, instruction) } @@ -228,9 +236,12 @@ func (c *configurator) runUserPostRules(ctx context.Context, filepath string, re rule = flipRule(rule) } - if ipv4 { + switch { + case ipv4: err = c.runIptablesInstruction(ctx, rule) - } else { + case !c.ip6Tables: + err = fmt.Errorf("cannot run user ip6tables rule: %w", ErrNeedIP6Tables) + default: // ipv6 err = c.runIP6tablesInstruction(ctx, rule) } if err != nil {