Manually choosing xtables or nftables - issues with openvz

I have recently brought a vps, and its running openvz of some version and said it supported docker.

I have tried to get lxd working and im struggling with the network side as it doesnt seem to support the inet type that lxd is using in the rule

so lxd tries to put in

table inet lxd {

        chain pstrt.lxdbr0 {
                type nat hook postrouting priority srcnat; policy accept;
                ip saddr 192.168.240.0/24 ip daddr != 192.168.240.0/24 masquerade
        }
}

if i change the inet for ip it goes in ok

I believe this is a host kernel issue so not fixable. If I disable the part that adds rules etc then it looks like I can get things working, but thats annoying and losing a lot of the benefits of lxd

I would therefore like to select the xtables interface to use, unfortunately this is a ubuntu 22.04 vps so lxd is a snap, so not easy to change things.

Ive had a quick look but cant see any obvious way for a configuration way to force the xtables use. (have i missed it)

does anyone have a solution?

Thanks

Simon

LXD will use xtables if any xtables rule exists at the time it’s started.
So just adding a pointless xtables rule should do the trick.

Should I look at submitting a bug report as lxd fails to work when nftables doesnt support the combined inet type rather than separate ip and ip6 types. I know most modern kernels shouldnt have an issue but this is a recently purchased vps (ok openvz style) and I assume not running on that outdated versions, so its not that this is some horribly old thing that been running for years

I suspect the testing for nftables functionality should try adding an inet rule and if it fails fall back to xtables, rather than force the user to fudge it with an iptables rule before lxd starts

Yeah, we can take a look to see if we can detect such systems and fallback to xtables in such cases.
We already have a number of kernel version checks to detect broken nft systems.

Unfortunately I cant get it to accept the xtables (it occasionally happens but I reboot node and insert the iptables rules (from a file) and most of the time it doesnt work.

So I have looked at the code in

lxd/firewall/firewall_load.go

line 14 it checks if there is any issues with nft (via compat function) and if not it then on line 23 does an xtables compatible check running the Compat function

looking the the xtables driver the first thing it does is check if the iptables command is an nft shim and if so fails back therefore creating xtables rules wont work if your iptables command is an nft shim.

So to be honest I have no idea how it decided a couple of times to actually not use the nftables as iptables has always been a shim.

So improved detection for inserting a chain similar to what I entered is needed. I think it might be wise to include a flag to force one mode or the other as that would allow people to work around edge cases without the need to get new code changes inserted.

Im not a Go developer so not sure how to add switches to force this but I have made some changes to the drivers_nftables.go file to detect this edge case

func (d Nftables) Compat() (bool, error) {
        // Get the kernel version.
        uname, err := shared.Uname()
        if err != nil {
                return false, err
        }

        uname, er := shared.Uname()
        if er != nil {
                return false, er
        }

the last 4 lines are new and used to run command but not loose previous error message

        _, err = shared.RunCommandCLocale("nft", "create", "table", testTable)
        if err != nil {
                return false, fmt.Errorf("Failed to create a test table: %w", err)
        }

//        nft add rule inet testable5 prert.test ip saddr 192.168.24.0/24 ip daddr != 192.168.24.0/24 masquerade
        _, err = shared.RunCommandCLocale("nft", "create", "table", "inet", testTable)
        if err != nil {
                return false, fmt.Errorf("Failed to create an inet test table: %w", err)
        }

        _, err = shared.RunCommandCLocale("nft", "add", "chain", "inet", testTable, "prert.test")
        if err != nil {
                return false, fmt.Errorf("Failed to create an inet test chain in table: %w", err)
        }

        _, err = shared.RunCommandCLocale("nft", "add", "rule", "inet", testTable, "prert.test", "ip", "saddr", "192.168.24.0/24", "ip", "daddr", "!=", "192.168.24.0/24", "masquerade")
        if err != nil {
                _, er = shared.RunCommandCLocale("nft", "delete", "chain", "inet", testTable, "prert.test")
                _, er = shared.RunCommandCLocale("nft", "delete", "table", "inet", testTable)
                _, er = shared.RunCommandCLocale("nft", "delete", "table", testTable)
                return false, fmt.Errorf("Failed to create an inet test chain rule in table: %w", err)
        }


        _, err = shared.RunCommandCLocale("nft", "delete", "table", testTable)
        if err != nil {
                return false, fmt.Errorf("Failed to delete a test table: %w", err)
        }

between the 2 existing tests I have added a new one (I cannot run the new lxd on the good server due to production but I did test the nft command failed on the 5.2.0 kernel without inet support in nftables, but sucessfully runs on a 5.15.x kernel so I assume its a good check and fails due to lack of inet support

the test I have added was very similar to what lxd itself inserts upon doing nat with nftables

With these changes my system doesnt automatically just use the nftables (as if nftables works and iptables uses the shim modules then logic to me says it will allways select nftables)

Simon