From fbea8aee170ba0db3255a873f53bbf82e14a603f Mon Sep 17 00:00:00 2001 From: Francesco Cheinasso Date: Thu, 4 Jan 2024 18:28:15 +0100 Subject: [PATCH 1/2] NAT: prefix test --- nftables_test.go | 57 +++++++++++++++++++++++++++++ util.go | 33 +++++++++++++++++ util_test.go | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 util_test.go diff --git a/nftables_test.go b/nftables_test.go index 160d12d..743cde6 100644 --- a/nftables_test.go +++ b/nftables_test.go @@ -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,61 @@ func TestConfigureNAT(t *testing.T) { }, }) + dstipmatch, dstcidrmatch, err := net.ParseCIDR("10.0.0.0/24") + if err != nil { + t.Fatal(err) + } + + dnatfirstip, err := nftables.GetFirstIPFromCIDR("20.0.0.0/24") + if err != nil { + t.Fatal(err) + } + + dnatlastip, err := nftables.GetLastIPFromCIDR("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, + 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) } diff --git a/util.go b/util.go index b0576e7..6122814 100644 --- a/util.go +++ b/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,35 @@ func (genmsg *NFGenMsg) Decode(b []byte) { genmsg.Version = b[1] genmsg.ResourceID = binary.BigEndian.Uint16(b[2:]) } + +// GetFirstIPFromCIDR returns the first IP address from a CIDR. +func GetFirstIPFromCIDR(cidr string) (*net.IP, error) { + _, subnet, err := net.ParseCIDR(cidr) + if err != nil { + return nil, err + } + + mask := binary.BigEndian.Uint32(subnet.Mask) + ip := binary.BigEndian.Uint32(subnet.IP) + + // find the final address + firstIP := make(net.IP, 4) + binary.BigEndian.PutUint32(firstIP, ip&mask) + + return &firstIP, nil +} + +// GetLastIPFromCIDR returns the last IP address from a CIDR. +func GetLastIPFromCIDR(cidr string) (*net.IP, error) { + _, subnet, err := net.ParseCIDR(cidr) + if err != nil { + return nil, err + } + mask := binary.BigEndian.Uint32(subnet.Mask) + ip := binary.BigEndian.Uint32(subnet.IP) + // find the final address + lastIP := make(net.IP, 4) + binary.BigEndian.PutUint32(lastIP, (ip&mask)|(mask^0xffffffff)) + + return &lastIP, nil +} diff --git a/util_test.go b/util_test.go new file mode 100644 index 0000000..d2747ad --- /dev/null +++ b/util_test.go @@ -0,0 +1,93 @@ +package nftables + +import ( + "net" + "reflect" + "testing" +) + +func TestGetFirstIPFromCIDR(t *testing.T) { + type args struct { + cidr string + } + tests := []struct { + name string + args args + want *net.IP + wantErr bool + }{ + { + name: "Test 0", + args: args{cidr: "fakecidr"}, + want: nil, + wantErr: true, + }, + { + name: "Test 1", + args: args{cidr: "10.0.0.0/24"}, + want: &net.IP{10, 0, 0, 0}, + wantErr: false, + }, + { + name: "Test 2", + args: args{cidr: "10.0.0.20/24"}, + want: &net.IP{10, 0, 0, 0}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GetFirstIPFromCIDR(tt.args.cidr) + if (err != nil) != tt.wantErr { + t.Errorf("GetFirstIPFromCIDR() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetFirstIPFromCIDR() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGetLastIPFromCIDR(t *testing.T) { + type args struct { + cidr string + } + tests := []struct { + name string + args args + want *net.IP + wantErr bool + }{ + { + name: "Test 0", + args: args{cidr: "fakecidr"}, + want: nil, + wantErr: true, + }, + { + name: "Test 1", + args: args{cidr: "10.0.0.0/24"}, + want: &net.IP{10, 0, 0, 255}, + wantErr: false, + }, + { + name: "Test 2", + args: args{cidr: "10.0.0.20/24"}, + want: &net.IP{10, 0, 0, 255}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GetLastIPFromCIDR(tt.args.cidr) + if (err != nil) != tt.wantErr { + t.Errorf("GetLastIPFromCIDR() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetLastIPFromCIDR() = %v, want %v", got, tt.want) + } + }) + } +} From 3f57b17eb3fc9d73184f8c1d41a96fe41dc6c3d5 Mon Sep 17 00:00:00 2001 From: Francesco Cheinasso Date: Thu, 11 Jan 2024 10:43:17 +0100 Subject: [PATCH 2/2] fix --- nftables_test.go | 17 +++---- util.go | 50 +++++++++++---------- util_test.go | 113 ++++++++++++++++++++--------------------------- 3 files changed, 83 insertions(+), 97 deletions(-) diff --git a/nftables_test.go b/nftables_test.go index 743cde6..d0ec731 100644 --- a/nftables_test.go +++ b/nftables_test.go @@ -454,12 +454,7 @@ func TestConfigureNAT(t *testing.T) { t.Fatal(err) } - dnatfirstip, err := nftables.GetFirstIPFromCIDR("20.0.0.0/24") - if err != nil { - t.Fatal(err) - } - - dnatlastip, err := nftables.GetLastIPFromCIDR("20.0.0.0/24") + dnatfirstip, dnatlastip, err := nftables.GetFirstAndLastIPFromCIDR("20.0.0.0/24") if err != nil { t.Fatal(err) } @@ -478,8 +473,10 @@ func TestConfigureNAT(t *testing.T) { SourceRegister: 1, DestRegister: 1, Len: 4, - Xor: []byte{0x0, 0x0, 0x0, 0x0}, - Mask: dstcidrmatch.Mask, + // 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, @@ -488,11 +485,11 @@ func TestConfigureNAT(t *testing.T) { }, &expr.Immediate{ Register: 1, - Data: *dnatfirstip, + Data: dnatfirstip, }, &expr.Immediate{ Register: 2, - Data: *dnatlastip, + Data: dnatlastip, }, &expr.NAT{ Type: expr.NATTypeDestNAT, diff --git a/util.go b/util.go index 6122814..6fd7c39 100644 --- a/util.go +++ b/util.go @@ -46,34 +46,38 @@ func (genmsg *NFGenMsg) Decode(b []byte) { genmsg.ResourceID = binary.BigEndian.Uint16(b[2:]) } -// GetFirstIPFromCIDR returns the first IP address from a CIDR. -func GetFirstIPFromCIDR(cidr string) (*net.IP, error) { +// GetFirstAndLastIPFromCIDR returns the first and last IP address from a CIDR. +func GetFirstAndLastIPFromCIDR(cidr string) (firstIP, lastIP net.IP, err error) { _, subnet, err := net.ParseCIDR(cidr) if err != nil { - return nil, err + return nil, nil, err } - mask := binary.BigEndian.Uint32(subnet.Mask) - ip := binary.BigEndian.Uint32(subnet.IP) + firstIP = make(net.IP, len(subnet.IP)) + lastIP = make(net.IP, len(subnet.IP)) - // find the final address - firstIP := make(net.IP, 4) - binary.BigEndian.PutUint32(firstIP, ip&mask) - - return &firstIP, nil -} - -// GetLastIPFromCIDR returns the last IP address from a CIDR. -func GetLastIPFromCIDR(cidr string) (*net.IP, error) { - _, subnet, err := net.ParseCIDR(cidr) - if err != nil { - return nil, err + 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(firstIP, 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(lastIP, (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(firstIP[:8], ip1&mask1) + binary.BigEndian.PutUint64(firstIP[8:], ip2&mask2) + binary.BigEndian.PutUint64(lastIP[:8], (ip1&mask1)|(mask1^0xffffffffffffffff)) + binary.BigEndian.PutUint64(lastIP[8:], (ip2&mask2)|(mask2^0xffffffffffffffff)) } - mask := binary.BigEndian.Uint32(subnet.Mask) - ip := binary.BigEndian.Uint32(subnet.IP) - // find the final address - lastIP := make(net.IP, 4) - binary.BigEndian.PutUint32(lastIP, (ip&mask)|(mask^0xffffffff)) - return &lastIP, nil + return firstIP, lastIP, nil } diff --git a/util_test.go b/util_test.go index d2747ad..8284736 100644 --- a/util_test.go +++ b/util_test.go @@ -6,87 +6,72 @@ import ( "testing" ) -func TestGetFirstIPFromCIDR(t *testing.T) { +func TestGetFirstAndLastIPFromCIDR(t *testing.T) { type args struct { cidr string } tests := []struct { - name string - args args - want *net.IP - wantErr bool + name string + args args + wantFirstIP net.IP + wantLastIP net.IP + wantErr bool }{ { - name: "Test 0", - args: args{cidr: "fakecidr"}, - want: nil, - wantErr: true, + name: "Test Fake", + args: args{cidr: "fakecidr"}, + wantFirstIP: nil, + wantLastIP: nil, + wantErr: true, }, { - name: "Test 1", - args: args{cidr: "10.0.0.0/24"}, - want: &net.IP{10, 0, 0, 0}, - wantErr: false, + 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 2", - args: args{cidr: "10.0.0.20/24"}, - want: &net.IP{10, 0, 0, 0}, - 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) { - got, err := GetFirstIPFromCIDR(tt.args.cidr) + gotFirstIP, gotLastIP, err := GetFirstAndLastIPFromCIDR(tt.args.cidr) if (err != nil) != tt.wantErr { - t.Errorf("GetFirstIPFromCIDR() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("GetFirstAndLastIPFromCIDR() error = %v, wantErr %v", err, tt.wantErr) return } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("GetFirstIPFromCIDR() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestGetLastIPFromCIDR(t *testing.T) { - type args struct { - cidr string - } - tests := []struct { - name string - args args - want *net.IP - wantErr bool - }{ - { - name: "Test 0", - args: args{cidr: "fakecidr"}, - want: nil, - wantErr: true, - }, - { - name: "Test 1", - args: args{cidr: "10.0.0.0/24"}, - want: &net.IP{10, 0, 0, 255}, - wantErr: false, - }, - { - name: "Test 2", - args: args{cidr: "10.0.0.20/24"}, - want: &net.IP{10, 0, 0, 255}, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := GetLastIPFromCIDR(tt.args.cidr) - if (err != nil) != tt.wantErr { - t.Errorf("GetLastIPFromCIDR() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("GetLastIPFromCIDR() = %v, want %v", got, tt.want) + 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) } }) }