NAT: prefix test
This commit is contained in:
parent
ef45dd3322
commit
33ee8df9d8
|
@ -253,6 +253,8 @@ func TestConfigureNAT(t *testing.T) {
|
|||
[]byte("\x02\x00\x00\x00\x08\x00\x01\x00\x6e\x61\x74\x00\x0f\x00\x02\x00\x70\x72\x65\x72\x6f\x75\x74\x69\x6e\x67\x00\x00\x98\x01\x04\x80\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x06\x08\x00\x01\x00\x00\x00\x00\x01\x38\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x2c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x18\x00\x03\x80\x14\x00\x01\x00\x75\x70\x6c\x69\x6e\x6b\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x10\x08\x00\x01\x00\x00\x00\x00\x01\x2c\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x20\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0c\x00\x03\x80\x05\x00\x01\x00\x06\x00\x00\x00\x34\x00\x01\x80\x0c\x00\x01\x00\x70\x61\x79\x6c\x6f\x61\x64\x00\x24\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x02\x08\x00\x03\x00\x00\x00\x00\x02\x08\x00\x04\x00\x00\x00\x00\x02\x2c\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x20\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0c\x00\x03\x80\x06\x00\x01\x00\x0f\xe6\x00\x00\x2c\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x18\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x0c\x00\x02\x80\x08\x00\x01\x00\xc0\xa8\x17\x02\x2c\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x18\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x02\x0c\x00\x02\x80\x06\x00\x01\x00\x0f\xf0\x00\x00\x30\x00\x01\x80\x08\x00\x01\x00\x6e\x61\x74\x00\x24\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x02\x08\x00\x03\x00\x00\x00\x00\x01\x08\x00\x05\x00\x00\x00\x00\x02"),
|
||||
// nft add rule nat prerouting iifname uplink0 udp dport 4070-4090 dnat 192.168.23.2:4070-4090
|
||||
[]byte("\x02\x00\x00\x00\x08\x00\x01\x00\x6e\x61\x74\x00\x0f\x00\x02\x00\x70\x72\x65\x72\x6f\x75\x74\x69\x6e\x67\x00\x00\xf8\x01\x04\x80\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x06\x08\x00\x01\x00\x00\x00\x00\x01\x38\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x2c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x18\x00\x03\x80\x14\x00\x01\x00\x75\x70\x6c\x69\x6e\x6b\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x10\x08\x00\x01\x00\x00\x00\x00\x01\x2c\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x20\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0c\x00\x03\x80\x05\x00\x01\x00\x11\x00\x00\x00\x34\x00\x01\x80\x0c\x00\x01\x00\x70\x61\x79\x6c\x6f\x61\x64\x00\x24\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x02\x08\x00\x03\x00\x00\x00\x00\x02\x08\x00\x04\x00\x00\x00\x00\x02\x2c\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x20\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x05\x0c\x00\x03\x80\x06\x00\x01\x00\x0f\xe6\x00\x00\x2c\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x20\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x03\x0c\x00\x03\x80\x06\x00\x01\x00\x0f\xfa\x00\x00\x2c\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x18\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x0c\x00\x02\x80\x08\x00\x01\x00\xc0\xa8\x17\x02\x2c\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x18\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x02\x0c\x00\x02\x80\x06\x00\x01\x00\x0f\xe6\x00\x00\x2c\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x18\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x03\x0c\x00\x02\x80\x06\x00\x01\x00\x0f\xfa\x00\x00\x38\x00\x01\x80\x08\x00\x01\x00\x6e\x61\x74\x00\x2c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x02\x08\x00\x03\x00\x00\x00\x00\x01\x08\x00\x05\x00\x00\x00\x00\x02\x08\x00\x06\x00\x00\x00\x00\x03"),
|
||||
// nft add rule nat prerouting ip daddr 10.0.0.0/24 dnat prefix to 20.0.0.0/24
|
||||
[]byte("\x02\x00\x00\x00\x08\x00\x01\x00\x6e\x61\x74\x00\x0f\x00\x02\x00\x70\x72\x65\x72\x6f\x75\x74\x69\x6e\x67\x00\x00\x38\x01\x04\x80\x34\x00\x01\x80\x0c\x00\x01\x00\x70\x61\x79\x6c\x6f\x61\x64\x00\x24\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x01\x08\x00\x03\x00\x00\x00\x00\x10\x08\x00\x04\x00\x00\x00\x00\x04\x44\x00\x01\x80\x0c\x00\x01\x00\x62\x69\x74\x77\x69\x73\x65\x00\x34\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x01\x08\x00\x03\x00\x00\x00\x00\x04\x0c\x00\x04\x80\x08\x00\x01\x00\xff\xff\xff\x00\x0c\x00\x05\x80\x08\x00\x01\x00\x00\x00\x00\x00\x2c\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x20\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0c\x00\x03\x80\x08\x00\x01\x00\x0a\x00\x00\x00\x2c\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x18\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x0c\x00\x02\x80\x08\x00\x01\x00\x14\x00\x00\x00\x2c\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x18\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x02\x0c\x00\x02\x80\x08\x00\x01\x00\x14\x00\x00\xff\x38\x00\x01\x80\x08\x00\x01\x00\x6e\x61\x74\x00\x2c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x02\x08\x00\x03\x00\x00\x00\x00\x01\x08\x00\x04\x00\x00\x00\x00\x02\x08\x00\x07\x00\x00\x00\x00\x40"),
|
||||
// batch end
|
||||
[]byte("\x00\x00\x00\x0a"),
|
||||
}
|
||||
|
@ -447,6 +449,58 @@ func TestConfigureNAT(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
dstipmatch, dstcidrmatch, err := net.ParseCIDR("10.0.0.0/24")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dnatfirstip, dnatlastip, err := nftables.NetFirstAndLastIP("20.0.0.0/24")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
c.AddRule(&nftables.Rule{
|
||||
Table: nat,
|
||||
Chain: prerouting,
|
||||
Exprs: []expr.Any{
|
||||
&expr.Payload{
|
||||
DestRegister: 1,
|
||||
Base: expr.PayloadBaseNetworkHeader,
|
||||
Offset: 16, // destination addr offset
|
||||
Len: 4,
|
||||
},
|
||||
&expr.Bitwise{
|
||||
SourceRegister: 1,
|
||||
DestRegister: 1,
|
||||
Len: 4,
|
||||
// By specifying Xor to 0x0,0x0,0x0,0x0 and Mask to the CIDR mask,
|
||||
// the rule will match the CIDR of the IP (e.g in this case 10.0.0.0/24).
|
||||
Xor: []byte{0x0, 0x0, 0x0, 0x0},
|
||||
Mask: dstcidrmatch.Mask,
|
||||
},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpEq,
|
||||
Register: 1,
|
||||
Data: dstipmatch.To4(),
|
||||
},
|
||||
&expr.Immediate{
|
||||
Register: 1,
|
||||
Data: dnatfirstip,
|
||||
},
|
||||
&expr.Immediate{
|
||||
Register: 2,
|
||||
Data: dnatlastip,
|
||||
},
|
||||
&expr.NAT{
|
||||
Type: expr.NATTypeDestNAT,
|
||||
RegAddrMin: 1,
|
||||
RegAddrMax: 2,
|
||||
Prefix: true,
|
||||
Family: uint32(nftables.TableFamilyIPv4),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if err := c.Flush(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
43
util.go
43
util.go
|
@ -16,6 +16,7 @@ package nftables
|
|||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
|
||||
"github.com/google/nftables/binaryutil"
|
||||
"golang.org/x/sys/unix"
|
||||
|
@ -44,3 +45,45 @@ func (genmsg *NFGenMsg) Decode(b []byte) {
|
|||
genmsg.Version = b[1]
|
||||
genmsg.ResourceID = binary.BigEndian.Uint16(b[2:])
|
||||
}
|
||||
|
||||
// NetFirstAndLastIP takes the beginning address of an entire network in CIDR
|
||||
// notation (e.g. 192.168.1.0/24) and returns the first and last IP addresses
|
||||
// within the network (e.g. first 192.168.1.0, last 192.168.1.255).
|
||||
//
|
||||
// Note that these are the first and last IP addresses, not the first and last
|
||||
// *usable* IP addresses (which would be 192.168.1.1 and 192.168.1.254,
|
||||
// respectively, for 192.168.1.0/24).
|
||||
func NetFirstAndLastIP(networkCIDR string) (first, last net.IP, err error) {
|
||||
_, subnet, err := net.ParseCIDR(networkCIDR)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
first = make(net.IP, len(subnet.IP))
|
||||
last = make(net.IP, len(subnet.IP))
|
||||
|
||||
switch len(subnet.IP) {
|
||||
case net.IPv4len:
|
||||
mask := binary.BigEndian.Uint32(subnet.Mask)
|
||||
ip := binary.BigEndian.Uint32(subnet.IP)
|
||||
// To achieve the first IP address, we need to AND the IP with the mask.
|
||||
// The AND operation will set all bits in the host part to 0.
|
||||
binary.BigEndian.PutUint32(first, ip&mask)
|
||||
// To achieve the last IP address, we need to OR the IP network with the inverted mask.
|
||||
// The AND between the IP and the mask will set all bits in the host part to 0, keeping the network part.
|
||||
// The XOR between the mask and 0xffffffff will set all bits in the host part to 1, and the network part to 0.
|
||||
// The OR operation will keep the host part unchanged, and sets the host part to all 1.
|
||||
binary.BigEndian.PutUint32(last, (ip&mask)|(mask^0xffffffff))
|
||||
case net.IPv6len:
|
||||
mask1 := binary.BigEndian.Uint64(subnet.Mask[:8])
|
||||
mask2 := binary.BigEndian.Uint64(subnet.Mask[8:])
|
||||
ip1 := binary.BigEndian.Uint64(subnet.IP[:8])
|
||||
ip2 := binary.BigEndian.Uint64(subnet.IP[8:])
|
||||
binary.BigEndian.PutUint64(first[:8], ip1&mask1)
|
||||
binary.BigEndian.PutUint64(first[8:], ip2&mask2)
|
||||
binary.BigEndian.PutUint64(last[:8], (ip1&mask1)|(mask1^0xffffffffffffffff))
|
||||
binary.BigEndian.PutUint64(last[8:], (ip2&mask2)|(mask2^0xffffffffffffffff))
|
||||
}
|
||||
|
||||
return first, last, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package nftables
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNetFirstAndLastIP(t *testing.T) {
|
||||
type args struct {
|
||||
cidr string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantFirstIP net.IP
|
||||
wantLastIP net.IP
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Test Fake",
|
||||
args: args{cidr: "fakecidr"},
|
||||
wantFirstIP: nil,
|
||||
wantLastIP: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Test IPV4 1",
|
||||
args: args{cidr: "10.0.0.0/24"},
|
||||
wantFirstIP: net.IP{10, 0, 0, 0},
|
||||
wantLastIP: net.IP{10, 0, 0, 255},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Test IPV4 2",
|
||||
args: args{cidr: "10.0.0.20/24"},
|
||||
wantFirstIP: net.IP{10, 0, 0, 0},
|
||||
wantLastIP: net.IP{10, 0, 0, 255},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Test IPV4 2",
|
||||
args: args{cidr: "10.0.0.0/19"},
|
||||
wantFirstIP: net.IP{10, 0, 0, 0},
|
||||
wantLastIP: net.IP{10, 0, 31, 255},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Test IPV6 1",
|
||||
args: args{cidr: "ff00::/16"},
|
||||
wantFirstIP: net.ParseIP("ff00::"),
|
||||
wantLastIP: net.ParseIP("ff00:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Test IPV6 2",
|
||||
args: args{cidr: "2001:db8::/62"},
|
||||
wantFirstIP: net.ParseIP("2001:db8::"),
|
||||
wantLastIP: net.ParseIP("2001:db8:0000:0003:ffff:ffff:ffff:ffff"),
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotFirstIP, gotLastIP, err := NetFirstAndLastIP(tt.args.cidr)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("GetFirstAndLastIPFromCIDR() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(gotFirstIP, tt.wantFirstIP) {
|
||||
t.Errorf("GetFirstAndLastIPFromCIDR() gotFirstIP = %v, want %v", gotFirstIP, tt.wantFirstIP)
|
||||
}
|
||||
if !reflect.DeepEqual(gotLastIP, tt.wantLastIP) {
|
||||
t.Errorf("GetFirstAndLastIPFromCIDR() gotLastIP = %v, want %v", gotLastIP, tt.wantLastIP)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue