-
Notifications
You must be signed in to change notification settings - Fork 163
Description
Hi,
nft will print intervals in CIDR representation - for example:
inet filter @testset6
element 000080fe 00000000 00000000 02000000 : 1 [end]
element 000080fe 00000000 00000000 01000000 : 0 [end]
element 0000072a 01000000 00000000 00000000 : 1 [end]
element 0000072a 00000000 00000000 00000000 : 0 [end]
element 00000000 00000000 00000000 00000000 : 1 [end]
inet filter @testset4
element 0001a8c0 : 1 [end]
element 0000a8c0 : 0 [end]
element 0200007f : 1 [end]
element 0100007f : 0 [end]
element 0200000a : 1 [end]
element 0100000a : 0 [end]
element 00000000 : 1 [end]
gets turned into:
table inet filter {
set testset6 {
type ipv6_addr
flags interval
elements = { 2a07::/64,
fe80::1 }
}
set testset4 {
type ipv4_addr
flags interval
elements = { 10.0.0.1, 127.0.0.1,
192.168.0.0/24 }
}
}
Thanks to #342 it is now easy to add new elements in this format.
I would appreciate some advice in regards to retrieving elements in the same representation.
One issue is #320, but it can be worked around by iterating over the returned elements in reverse order (hoping the order is stable).
So far I came up with the following (omitted set discovery and error handling for brevity):
elements, _ := nft.GetSetElements(set)
var (
out []string // will contain the resulting nft-style elements
last nftables.SetElement
)
NullIPv4 := []byte{0, 0, 0, 0}
NullIPv6 := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
for i := len(elements)-1; i >= 0; i-- {
e := elements[i]
switch set.KeyType.Name {
case "ipv4_addr", "ipv6_addr":
if bytes.Compare(e.Key, NullIPv4) == 0 || bytes.Compare(e.Key, NullIPv6) == 0 {
continue
}
ipCurrent, _ := netip.AddrFromSlice(e.Key)
ipLast, _ := netip.AddrFromSlice(last.Key)
ipLastNext := ipLast.Next()
if e.IntervalEnd && ipCurrent == ipLastNext {
out = append(out, ipLast.String())
continue
}
if e.IntervalEnd {
maxLen := 32
if ipLastNext.Is6() {
if !ipCurrent.Is6() {
continue
}
maxLen = 128
}
for l := maxLen; l >= 0; l-- {
mask := net.CIDRMask(l, maxLen)
na := net.IP(ipLastNext.AsSlice()).Mask(mask)
n := net.IPNet{IP: na, Mask: mask}
if n.Contains(net.IP(ipCurrent.AsSlice())) {
out = append(out, fmt.Sprintf("%s/%d", na, l+1))
break
}
}
continue
}
last = e
}
}
Returns (in out):
["10.0.0.1","127.0.0.1","192.168.0.0/24"] // "testset4"
["2a07::/64","fe80::1"] // "testset6"
I would be interested if anyone has feedback and/or a better approach for this - the above feels rather clunky, and I'm not sure if I am not missing any cases (for example, the skipping of zero elements and the need for appending 1 to the mask feels wrong).
If there is a cleaner solution, possibly we could integrate it into the library (either directly into GetSetElements or into a helper function)? It would be useful to reduce the differences between nft and Go, particularly when using both for management.