github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/lang/funcs/cidr.go (about)

     1  package funcs
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  
     7  	"github.com/apparentlymart/go-cidr/cidr"
     8  	"github.com/zclconf/go-cty/cty"
     9  	"github.com/zclconf/go-cty/cty/function"
    10  	"github.com/zclconf/go-cty/cty/gocty"
    11  )
    12  
    13  // CidrHostFunc contructs a function that calculates a full host IP address
    14  // within a given IP network address prefix.
    15  var CidrHostFunc = function.New(&function.Spec{
    16  	Params: []function.Parameter{
    17  		{
    18  			Name: "prefix",
    19  			Type: cty.String,
    20  		},
    21  		{
    22  			Name: "hostnum",
    23  			Type: cty.Number,
    24  		},
    25  	},
    26  	Type: function.StaticReturnType(cty.String),
    27  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
    28  		var hostNum int
    29  		if err := gocty.FromCtyValue(args[1], &hostNum); err != nil {
    30  			return cty.UnknownVal(cty.String), err
    31  		}
    32  		_, network, err := net.ParseCIDR(args[0].AsString())
    33  		if err != nil {
    34  			return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err)
    35  		}
    36  
    37  		ip, err := cidr.Host(network, hostNum)
    38  		if err != nil {
    39  			return cty.UnknownVal(cty.String), err
    40  		}
    41  
    42  		return cty.StringVal(ip.String()), nil
    43  	},
    44  })
    45  
    46  // CidrNetmaskFunc contructs a function that converts an IPv4 address prefix given
    47  // in CIDR notation into a subnet mask address.
    48  var CidrNetmaskFunc = function.New(&function.Spec{
    49  	Params: []function.Parameter{
    50  		{
    51  			Name: "prefix",
    52  			Type: cty.String,
    53  		},
    54  	},
    55  	Type: function.StaticReturnType(cty.String),
    56  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
    57  		_, network, err := net.ParseCIDR(args[0].AsString())
    58  		if err != nil {
    59  			return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err)
    60  		}
    61  
    62  		return cty.StringVal(net.IP(network.Mask).String()), nil
    63  	},
    64  })
    65  
    66  // CidrSubnetFunc contructs a function that calculates a subnet address within
    67  // a given IP network address prefix.
    68  var CidrSubnetFunc = function.New(&function.Spec{
    69  	Params: []function.Parameter{
    70  		{
    71  			Name: "prefix",
    72  			Type: cty.String,
    73  		},
    74  		{
    75  			Name: "newbits",
    76  			Type: cty.Number,
    77  		},
    78  		{
    79  			Name: "netnum",
    80  			Type: cty.Number,
    81  		},
    82  	},
    83  	Type: function.StaticReturnType(cty.String),
    84  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
    85  		var newbits int
    86  		if err := gocty.FromCtyValue(args[1], &newbits); err != nil {
    87  			return cty.UnknownVal(cty.String), err
    88  		}
    89  		var netnum int
    90  		if err := gocty.FromCtyValue(args[2], &netnum); err != nil {
    91  			return cty.UnknownVal(cty.String), err
    92  		}
    93  
    94  		_, network, err := net.ParseCIDR(args[0].AsString())
    95  		if err != nil {
    96  			return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err)
    97  		}
    98  
    99  		// For portability with 32-bit systems where the subnet number
   100  		// will be a 32-bit int, we only allow extension of 32 bits in
   101  		// one call even if we're running on a 64-bit machine.
   102  		// (Of course, this is significant only for IPv6.)
   103  		if newbits > 32 {
   104  			return cty.UnknownVal(cty.String), fmt.Errorf("may not extend prefix by more than 32 bits")
   105  		}
   106  
   107  		newNetwork, err := cidr.Subnet(network, newbits, netnum)
   108  		if err != nil {
   109  			return cty.UnknownVal(cty.String), err
   110  		}
   111  
   112  		return cty.StringVal(newNetwork.String()), nil
   113  	},
   114  })
   115  
   116  // CidrSubnetsFunc is similar to CidrSubnetFunc but calculates many consecutive
   117  // subnet addresses at once, rather than just a single subnet extension.
   118  var CidrSubnetsFunc = function.New(&function.Spec{
   119  	Params: []function.Parameter{
   120  		{
   121  			Name: "prefix",
   122  			Type: cty.String,
   123  		},
   124  	},
   125  	VarParam: &function.Parameter{
   126  		Name: "newbits",
   127  		Type: cty.Number,
   128  	},
   129  	Type: function.StaticReturnType(cty.List(cty.String)),
   130  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
   131  		_, network, err := net.ParseCIDR(args[0].AsString())
   132  		if err != nil {
   133  			return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "invalid CIDR expression: %s", err)
   134  		}
   135  		startPrefixLen, _ := network.Mask.Size()
   136  
   137  		prefixLengthArgs := args[1:]
   138  		if len(prefixLengthArgs) == 0 {
   139  			return cty.ListValEmpty(cty.String), nil
   140  		}
   141  
   142  		var firstLength int
   143  		if err := gocty.FromCtyValue(prefixLengthArgs[0], &firstLength); err != nil {
   144  			return cty.UnknownVal(cty.String), function.NewArgError(1, err)
   145  		}
   146  		firstLength += startPrefixLen
   147  
   148  		retVals := make([]cty.Value, len(prefixLengthArgs))
   149  
   150  		current, _ := cidr.PreviousSubnet(network, firstLength)
   151  		for i, lengthArg := range prefixLengthArgs {
   152  			var length int
   153  			if err := gocty.FromCtyValue(lengthArg, &length); err != nil {
   154  				return cty.UnknownVal(cty.String), function.NewArgError(i+1, err)
   155  			}
   156  
   157  			if length < 1 {
   158  				return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "must extend prefix by at least one bit")
   159  			}
   160  			// For portability with 32-bit systems where the subnet number
   161  			// will be a 32-bit int, we only allow extension of 32 bits in
   162  			// one call even if we're running on a 64-bit machine.
   163  			// (Of course, this is significant only for IPv6.)
   164  			if length > 32 {
   165  				return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "may not extend prefix by more than 32 bits")
   166  			}
   167  			length += startPrefixLen
   168  			if length > (len(network.IP) * 8) {
   169  				protocol := "IP"
   170  				switch len(network.IP) * 8 {
   171  				case 32:
   172  					protocol = "IPv4"
   173  				case 128:
   174  					protocol = "IPv6"
   175  				}
   176  				return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "would extend prefix to %d bits, which is too long for an %s address", length, protocol)
   177  			}
   178  
   179  			next, rollover := cidr.NextSubnet(current, length)
   180  			if rollover || !network.Contains(next.IP) {
   181  				// If we run out of suffix bits in the base CIDR prefix then
   182  				// NextSubnet will start incrementing the prefix bits, which
   183  				// we don't allow because it would then allocate addresses
   184  				// outside of the caller's given prefix.
   185  				return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "not enough remaining address space for a subnet with a prefix of %d bits after %s", length, current.String())
   186  			}
   187  
   188  			current = next
   189  			retVals[i] = cty.StringVal(current.String())
   190  		}
   191  
   192  		return cty.ListVal(retVals), nil
   193  	},
   194  })
   195  
   196  // CidrHost calculates a full host IP address within a given IP network address prefix.
   197  func CidrHost(prefix, hostnum cty.Value) (cty.Value, error) {
   198  	return CidrHostFunc.Call([]cty.Value{prefix, hostnum})
   199  }
   200  
   201  // CidrNetmask converts an IPv4 address prefix given in CIDR notation into a subnet mask address.
   202  func CidrNetmask(prefix cty.Value) (cty.Value, error) {
   203  	return CidrNetmaskFunc.Call([]cty.Value{prefix})
   204  }
   205  
   206  // CidrSubnet calculates a subnet address within a given IP network address prefix.
   207  func CidrSubnet(prefix, newbits, netnum cty.Value) (cty.Value, error) {
   208  	return CidrSubnetFunc.Call([]cty.Value{prefix, newbits, netnum})
   209  }
   210  
   211  // CidrSubnets calculates a sequence of consecutive subnet prefixes that may
   212  // be of different prefix lengths under a common base prefix.
   213  func CidrSubnets(prefix cty.Value, newbits ...cty.Value) (cty.Value, error) {
   214  	args := make([]cty.Value, len(newbits)+1)
   215  	args[0] = prefix
   216  	copy(args[1:], newbits)
   217  	return CidrSubnetsFunc.Call(args)
   218  }