lang/funcs: update cidrsubnet and cidrhost for 64-bit systems (#25517)

* upgrade go-cidr to v1.1.0

* lang/funcs: refactor Subnet and Host functions to support 64-bit systems

* add test cases and remove no-longer-needed validation
This commit is contained in:
Kristin Laemmert 2020-08-11 11:51:31 -04:00 committed by GitHub
commit 4729f7f76b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 60 additions and 27 deletions

2
go.mod
View File

@ -12,7 +12,7 @@ require (
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190329064014-6e358769c32a github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190329064014-6e358769c32a
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190103054945-8205d1f41e70 github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190103054945-8205d1f41e70
github.com/aliyun/aliyun-tablestore-go-sdk v4.1.2+incompatible github.com/aliyun/aliyun-tablestore-go-sdk v4.1.2+incompatible
github.com/apparentlymart/go-cidr v1.0.1 github.com/apparentlymart/go-cidr v1.1.0
github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0
github.com/apparentlymart/go-userdirs v0.0.0-20190512014041-4a23807e62b9 github.com/apparentlymart/go-userdirs v0.0.0-20190512014041-4a23807e62b9
github.com/apparentlymart/go-versions v1.0.0 github.com/apparentlymart/go-versions v1.0.0

2
go.sum
View File

@ -76,6 +76,8 @@ github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0 h1:JaCC8jz0zdMLk2m+
github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0/go.mod h1:LzD22aAzDP8/dyiCKFp31He4m2GPjl0AFyzDtZzUu9M= github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0/go.mod h1:LzD22aAzDP8/dyiCKFp31He4m2GPjl0AFyzDtZzUu9M=
github.com/apparentlymart/go-cidr v1.0.1 h1:NmIwLZ/KdsjIUlhf+/Np40atNXm/+lZ5txfTJ/SpF+U= github.com/apparentlymart/go-cidr v1.0.1 h1:NmIwLZ/KdsjIUlhf+/Np40atNXm/+lZ5txfTJ/SpF+U=
github.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= github.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU=
github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFUye+ZcSR6opIgz9Co7WcDx6ZcY+RjfFHoA0I= github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFUye+ZcSR6opIgz9Co7WcDx6ZcY+RjfFHoA0I=
github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=

View File

@ -2,6 +2,7 @@ package funcs
import ( import (
"fmt" "fmt"
"math/big"
"net" "net"
"github.com/apparentlymart/go-cidr/cidr" "github.com/apparentlymart/go-cidr/cidr"
@ -25,7 +26,7 @@ var CidrHostFunc = function.New(&function.Spec{
}, },
Type: function.StaticReturnType(cty.String), Type: function.StaticReturnType(cty.String),
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
var hostNum int var hostNum *big.Int
if err := gocty.FromCtyValue(args[1], &hostNum); err != nil { if err := gocty.FromCtyValue(args[1], &hostNum); err != nil {
return cty.UnknownVal(cty.String), err return cty.UnknownVal(cty.String), err
} }
@ -34,7 +35,7 @@ var CidrHostFunc = function.New(&function.Spec{
return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err) return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err)
} }
ip, err := cidr.Host(network, hostNum) ip, err := cidr.HostBig(network, hostNum)
if err != nil { if err != nil {
return cty.UnknownVal(cty.String), err return cty.UnknownVal(cty.String), err
} }
@ -86,7 +87,7 @@ var CidrSubnetFunc = function.New(&function.Spec{
if err := gocty.FromCtyValue(args[1], &newbits); err != nil { if err := gocty.FromCtyValue(args[1], &newbits); err != nil {
return cty.UnknownVal(cty.String), err return cty.UnknownVal(cty.String), err
} }
var netnum int var netnum *big.Int
if err := gocty.FromCtyValue(args[2], &netnum); err != nil { if err := gocty.FromCtyValue(args[2], &netnum); err != nil {
return cty.UnknownVal(cty.String), err return cty.UnknownVal(cty.String), err
} }
@ -96,15 +97,7 @@ var CidrSubnetFunc = function.New(&function.Spec{
return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err) return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err)
} }
// For portability with 32-bit systems where the subnet number newNetwork, err := cidr.SubnetBig(network, newbits, netnum)
// will be a 32-bit int, we only allow extension of 32 bits in
// one call even if we're running on a 64-bit machine.
// (Of course, this is significant only for IPv6.)
if newbits > 32 {
return cty.UnknownVal(cty.String), fmt.Errorf("may not extend prefix by more than 32 bits")
}
newNetwork, err := cidr.Subnet(network, newbits, netnum)
if err != nil { if err != nil {
return cty.UnknownVal(cty.String), err return cty.UnknownVal(cty.String), err
} }

View File

@ -56,6 +56,12 @@ func TestCidrHost(t *testing.T) {
cty.UnknownVal(cty.String), cty.UnknownVal(cty.String),
true, // can't have an octet >255 true, // can't have an octet >255
}, },
{ // fractions are Not Ok
cty.StringVal("10.256.0.0/8"),
cty.NumberFloatVal(.75),
cty.UnknownVal(cty.String),
true,
},
} }
for _, test := range tests { for _, test := range tests {
@ -165,6 +171,13 @@ func TestCidrSubnet(t *testing.T) {
cty.StringVal("192.168.6.0/24"), cty.StringVal("192.168.6.0/24"),
false, false,
}, },
{
cty.StringVal("fe80::/48"),
cty.NumberIntVal(33),
cty.NumberIntVal(6),
cty.StringVal("fe80::3:0:0:0/81"),
false,
},
{ // not enough bits left { // not enough bits left
cty.StringVal("192.168.0.0/30"), cty.StringVal("192.168.0.0/30"),
cty.NumberIntVal(4), cty.NumberIntVal(4),
@ -176,21 +189,28 @@ func TestCidrSubnet(t *testing.T) {
cty.StringVal("192.168.0.0/168"), cty.StringVal("192.168.0.0/168"),
cty.NumberIntVal(2), cty.NumberIntVal(2),
cty.NumberIntVal(16), cty.NumberIntVal(16),
cty.StringVal("fe80:0:0:6::/64"), cty.UnknownVal(cty.String),
true, true,
}, },
{ // not a valid CIDR mask { // not a valid CIDR mask
cty.StringVal("not-a-cidr"), cty.StringVal("not-a-cidr"),
cty.NumberIntVal(4), cty.NumberIntVal(4),
cty.NumberIntVal(6), cty.NumberIntVal(6),
cty.StringVal("fe80:0:0:6::/64"), cty.UnknownVal(cty.String),
true, true,
}, },
{ // can't have an octet >255 { // can't have an octet >255
cty.StringVal("10.256.0.0/8"), cty.StringVal("10.256.0.0/8"),
cty.NumberIntVal(4), cty.NumberIntVal(4),
cty.NumberIntVal(6), cty.NumberIntVal(6),
cty.StringVal("fe80:0:0:6::/64"), cty.UnknownVal(cty.String),
true,
},
{ // fractions are Not Ok
cty.StringVal("10.256.0.0/8"),
cty.NumberFloatVal(2 / 3),
cty.NumberFloatVal(.75),
cty.UnknownVal(cty.String),
true, true,
}, },
} }

View File

@ -28,6 +28,16 @@ import (
// For example, 10.3.0.0/16, extended by 8 bits, with a network number // For example, 10.3.0.0/16, extended by 8 bits, with a network number
// of 5, becomes 10.3.5.0/24 . // of 5, becomes 10.3.5.0/24 .
func Subnet(base *net.IPNet, newBits int, num int) (*net.IPNet, error) { func Subnet(base *net.IPNet, newBits int, num int) (*net.IPNet, error) {
return SubnetBig(base, newBits, big.NewInt(int64(num)))
}
// SubnetBig takes a parent CIDR range and creates a subnet within it with the
// given number of additional prefix bits and the given network number. It
// differs from Subnet in that it takes a *big.Int for the num, instead of an int.
//
// For example, 10.3.0.0/16, extended by 8 bits, with a network number of 5,
// becomes 10.3.5.0/24 .
func SubnetBig(base *net.IPNet, newBits int, num *big.Int) (*net.IPNet, error) {
ip := base.IP ip := base.IP
mask := base.Mask mask := base.Mask
@ -39,24 +49,32 @@ func Subnet(base *net.IPNet, newBits int, num int) (*net.IPNet, error) {
} }
maxNetNum := uint64(1<<uint64(newBits)) - 1 maxNetNum := uint64(1<<uint64(newBits)) - 1
if uint64(num) > maxNetNum { if num.Uint64() > maxNetNum {
return nil, fmt.Errorf("prefix extension of %d does not accommodate a subnet numbered %d", newBits, num) return nil, fmt.Errorf("prefix extension of %d does not accommodate a subnet numbered %d", newBits, num)
} }
return &net.IPNet{ return &net.IPNet{
IP: insertNumIntoIP(ip, big.NewInt(int64(num)), newPrefixLen), IP: insertNumIntoIP(ip, num, newPrefixLen),
Mask: net.CIDRMask(newPrefixLen, addrLen), Mask: net.CIDRMask(newPrefixLen, addrLen),
}, nil }, nil
} }
// Host takes a parent CIDR range and turns it into a host IP address with // Host takes a parent CIDR range and turns it into a host IP address with the
// the given host number. // given host number.
// //
// For example, 10.3.0.0/16 with a host number of 2 gives 10.3.0.2. // For example, 10.3.0.0/16 with a host number of 2 gives 10.3.0.2.
func Host(base *net.IPNet, num int) (net.IP, error) { func Host(base *net.IPNet, num int) (net.IP, error) {
return HostBig(base, big.NewInt(int64(num)))
}
// HostBig takes a parent CIDR range and turns it into a host IP address with
// the given host number. It differs from Host in that it takes a *big.Int for
// the num, instead of an int.
//
// For example, 10.3.0.0/16 with a host number of 2 gives 10.3.0.2.
func HostBig(base *net.IPNet, num *big.Int) (net.IP, error) {
ip := base.IP ip := base.IP
mask := base.Mask mask := base.Mask
bigNum := big.NewInt(int64(num))
parentLen, addrLen := mask.Size() parentLen, addrLen := mask.Size()
hostLen := addrLen - parentLen hostLen := addrLen - parentLen
@ -65,11 +83,11 @@ func Host(base *net.IPNet, num int) (net.IP, error) {
maxHostNum.Lsh(maxHostNum, uint(hostLen)) maxHostNum.Lsh(maxHostNum, uint(hostLen))
maxHostNum.Sub(maxHostNum, big.NewInt(1)) maxHostNum.Sub(maxHostNum, big.NewInt(1))
numUint64 := big.NewInt(int64(bigNum.Uint64())) numUint64 := big.NewInt(int64(num.Uint64()))
if bigNum.Cmp(big.NewInt(0)) == -1 { if num.Cmp(big.NewInt(0)) == -1 {
numUint64.Neg(bigNum) numUint64.Neg(num)
numUint64.Sub(numUint64, big.NewInt(int64(1))) numUint64.Sub(numUint64, big.NewInt(int64(1)))
bigNum.Sub(maxHostNum, numUint64) num.Sub(maxHostNum, numUint64)
} }
if numUint64.Cmp(maxHostNum) == 1 { if numUint64.Cmp(maxHostNum) == 1 {
@ -81,7 +99,7 @@ func Host(base *net.IPNet, num int) (net.IP, error) {
} else { } else {
bitlength = 128 bitlength = 128
} }
return insertNumIntoIP(ip, bigNum, bitlength), nil return insertNumIntoIP(ip, num, bitlength), nil
} }
// AddressRange returns the first and last addresses in the given CIDR range. // AddressRange returns the first and last addresses in the given CIDR range.

2
vendor/modules.txt vendored
View File

@ -87,7 +87,7 @@ github.com/aliyun/aliyun-tablestore-go-sdk/tablestore/search
github.com/antchfx/xpath github.com/antchfx/xpath
# github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0 # github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0
github.com/antchfx/xquery/xml github.com/antchfx/xquery/xml
# github.com/apparentlymart/go-cidr v1.0.1 # github.com/apparentlymart/go-cidr v1.1.0
## explicit ## explicit
github.com/apparentlymart/go-cidr/cidr github.com/apparentlymart/go-cidr/cidr
# github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 # github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0