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 }