github.com/hugorut/terraform@v1.1.3/src/lang/funcs/cidr.go (about)

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