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 }