Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AddSet IPv4 wrong byte order on Ubuntu 22.04 #225

Closed
eldevel opened this issue May 15, 2023 · 8 comments
Closed

AddSet IPv4 wrong byte order on Ubuntu 22.04 #225

eldevel opened this issue May 15, 2023 · 8 comments

Comments

@eldevel
Copy link

eldevel commented May 15, 2023

I am trying to add a set with ipv4 addresses, but the ip address bytes are reversed. Here is my code:

package main

import (
	"github.com/google/nftables"
	"github.com/vishvananda/netns"
	"net"
)

func main() {
	ns, _ := netns.Get()
	conn := nftables.Conn{NetNS: int(ns)}

	conn.FlushRuleset()

	mangle := conn.AddTable(&nftables.Table{
		Name:   "mangle",
		Family: nftables.TableFamilyINet,
	})

	testSet := &nftables.Set{
		Name:    "test_v4",
		Table:   mangle,
		KeyType: nftables.TypeIPAddr,
	}

	conn.AddSet(testSet, []nftables.SetElement{{
		Key: []byte(net.ParseIP("192.168.1.64").To4()),
	}})

	conn.Flush()
}

But the result is:

# nft list ruleset
table inet mangle {
        set test_v4 {
                type ipv4_addr
                elements = { 64.1.168.192 }
        }
}

Some additional information:

# go version
go version go1.18.1 linux/amd64

# nft -v
nftables v1.0.2 (Lester Gooch)

# uname -r -v
5.15.0-71-generic #78-Ubuntu SMP Tue Apr 18 09:00:29 UTC 2023

# cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=22.04
DISTRIB_CODENAME=jammy
DISTRIB_DESCRIPTION="Ubuntu 22.04.2 LTS

# lscpu | grep Order
Byte Order:                      Little Endian
@DamienDelporte
Copy link

DamienDelporte commented May 15, 2023

+1 Was about to open the same exact issue, I have reversed IPs in my set too 👍

$ go version
go version go1.19 linux/amd64

$ nft -v
nftables v0.9.3 (Topsy)

$ uname -r -v
5.14.0-1059-oem #67-Ubuntu SMP Mon Mar 13 14:22:10 UTC 2023

$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=20.04
DISTRIB_CODENAME=focal
DISTRIB_DESCRIPTION="Ubuntu 20.04.6 LTS"

$ lscpu | grep Order
Byte Order:                      Little Endian

@stapelberg
Copy link
Collaborator

Thanks for the report. Pull requests with bugfixes welcome (I don’t have time to look into this issue myself, but maybe some other contributor does…)

@turekt
Copy link
Contributor

turekt commented May 15, 2023

Hi to all,

here is the code responsible for this:

nftables/set.go

Lines 563 to 571 in b18665a

if s.Anonymous || s.Constant || s.Interval {
tableInfo = append(tableInfo,
// Semantically useless - kept for binary compatability with nft
netlink.Attribute{Type: unix.NFTA_SET_USERDATA, Data: []byte("\x00\x04\x02\x00\x00\x00")})
} else if !s.IsMap {
// Per https://git.netfilter.org/nftables/tree/src/mnl.c?id=187c6d01d35722618c2711bbc49262c286472c8f#n1165
tableInfo = append(tableInfo,
netlink.Attribute{Type: unix.NFTA_SET_USERDATA, Data: []byte("\x00\x04\x01\x00\x00\x00")})
}

I've checked debug messages for both your provided code and standard nft commands.
This code should fix your issue (note how Constant is set to true):

# cat main.go 
package main

import (
	"github.com/google/nftables"
	"github.com/vishvananda/netns"
	"net"
)

func main() {
	ns, _ := netns.Get()
	conn := nftables.Conn{NetNS: int(ns)}

	conn.FlushRuleset()

	mangle := conn.AddTable(&nftables.Table{
		Name:   "mangle",
		Family: nftables.TableFamilyINet,
	})

	testSet := &nftables.Set{
		Name:     "test_v44",
		Table:    mangle,
		KeyType:  nftables.TypeIPAddr,
		Constant: true,
	}

	conn.AddSet(testSet, []nftables.SetElement{{
		Key: []byte(net.ParseIP("127.0.0.1").To4()),
	}})

	conn.Flush()
}
# go run main.go
# nft list ruleset
table inet mangle {
	set test_v44 {
		type ipv4_addr
		flags constant
		elements = { 127.0.0.1 }
	}
}

Please give me a few more days to research this, the above code should quickly fix your issue.

@DamienDelporte
Copy link

Some dirty fix for ipv4 (I will do ipv6 later) to revert the revert 🙃

ip_rev := net.IPv4(ip[3], ip[2], ip[1], ip[0]).To4()
if err := conn.SetAddElements(set, []nftables.SetElement{{Key: ip_rev}}); err != nil {
		log.Println("conn.SetAddElements failed: %v", err)
}

I needed to add dynamically in set IP (so constant was not a solution in my case) :)

@eldevel
Copy link
Author

eldevel commented May 16, 2023

It looks like intervals are broken, too. At least I could not set any ipv4 interval.

turekt added a commit to turekt/nftables that referenced this issue May 16, 2023
Fixes google#225
Introduced KeyByteOrder in sets which fills UDATA with endianess information
@turekt
Copy link
Contributor

turekt commented May 16, 2023

Hi all,

I've looked into this further and this is what I think is the root cause: PR #180 was implemented to conform to the main nftables C implementation and it seemed that it fixed one small issue pointed out in #177. This change makes sense, but lib users cannot easily instruct nftables which endianess to use except by using current bool flags found in Set struct. This is not really flexible.

I have introduced KeyByteOrder in PR #226 which basically reverts PR #180 and allows users to specify whether set key endianess is big endian or host endian. This approach is more flexible and backwards compatible since not specifying KeyByteOrder will marshal bytes the same way as it was prior to PR #180. In other words, this issue should be resolved after merge immediately and issue #177 can still be fixed by setting KeyByteOrder to binaryutil.NativeEndian.

This approach also conforms to main nftables C implementation:

Feel free to test the PR branch and let me know whether it fixes your issue.

@eldevel
Copy link
Author

eldevel commented May 17, 2023

After debugging, I found that the intervals worked. I just didn't understand the KeyEnd attribute correctly. Actually KeyEnd is not used to set the interval, instead I had to add 2 elements (first and last). Here is the correct example:

conn.AddSet(testSet, []nftables.SetElement{{
    Key:         []byte(net.ParseIP("192.168.1.0").To4()),
    IntervalEnd: false,
}, {
    Key:         []byte(net.ParseIP("192.168.2.0").To4()),
    IntervalEnd: true,
}})

Result:

table inet mangle {
        set localnet4 {
                type ipv4_addr
                flags interval
                elements = { 192.168.1.0/24 }
        }
}

@DamienDelporte
Copy link

Thank you for the fix it's perfect !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants