github.com/cloudfoundry-attic/garden-linux@v0.333.2-candidate/network/subnets/subnets.go (about) 1 // The subnets package provides a subnet pool from which networks may be dynamically acquired or 2 // statically reserved. 3 package subnets 4 5 import ( 6 "fmt" 7 "math" 8 "net" 9 "sync" 10 11 "github.com/cloudfoundry-incubator/garden-linux/linux_backend" 12 "github.com/pivotal-golang/lager" 13 ) 14 15 // Subnets provides a means of allocating subnets and associated IP addresses. 16 type Subnets interface { 17 // Allocates an IP address and associates it with a subnet. The subnet is selected by the given SubnetSelector. 18 // The IP address is selected by the given IPSelector. 19 // Returns a subnet, an IP address, and a boolean which is true if and only if this is the 20 // first IP address to be associated with this subnet. 21 // If either selector fails, an error is returned. 22 Acquire(SubnetSelector, IPSelector, lager.Logger) (*linux_backend.Network, error) 23 24 // Releases an IP address associated with an allocated subnet. If the subnet has no other IP 25 // addresses associated with it, it is deallocated. 26 // Returns a boolean which is true if and only if the subnet was deallocated. 27 // Returns an error if the given combination is not already in the pool. 28 Release(*linux_backend.Network, lager.Logger) error 29 30 // Remove an IP address so it appears to be associated with the given subnet. 31 Remove(*linux_backend.Network, lager.Logger) error 32 33 // Returns the number of /30 subnets which can be Acquired by a DynamicSubnetSelector. 34 Capacity() int 35 } 36 37 type pool struct { 38 allocated map[string][]net.IP // net.IPNet.String +> seq net.IP 39 dynamicRange *net.IPNet 40 mu sync.Mutex 41 } 42 43 //go:generate counterfeiter . SubnetSelector 44 45 // SubnetSelector is a strategy for selecting a subnet. 46 type SubnetSelector interface { 47 // Returns a subnet based on a dynamic range and some existing statically-allocated 48 // subnets. If no suitable subnet can be found, returns an error. 49 SelectSubnet(dynamic *net.IPNet, existing []*net.IPNet) (*net.IPNet, error) 50 } 51 52 //go:generate counterfeiter . IPSelector 53 54 // IPSelector is a strategy for selecting an IP address in a subnet. 55 type IPSelector interface { 56 // Returns an IP address in the given subnet which is not one of the given existing 57 // IP addresses. If no such IP address can be found, returns an error. 58 SelectIP(subnet *net.IPNet, existing []net.IP) (net.IP, error) 59 } 60 61 // New creates a Subnets implementation from a dynamic allocation range. 62 // All dynamic allocations come from the range, static allocations are prohibited 63 // from the dynamic range. 64 func NewSubnets(ipNet *net.IPNet) (Subnets, error) { 65 return &pool{dynamicRange: ipNet, allocated: make(map[string][]net.IP)}, nil 66 } 67 68 // Acquire uses the given subnet and IP selectors to request a subnet, container IP address combination 69 // from the pool. 70 func (p *pool) Acquire(sn SubnetSelector, i IPSelector, logger lager.Logger) (network *linux_backend.Network, err error) { 71 p.mu.Lock() 72 defer p.mu.Unlock() 73 74 logger = logger.Session("acquire") 75 76 network = &linux_backend.Network{} 77 78 allocatedSubnets := subnets(p.allocated) 79 logger.Info("subnet-selecting", lager.Data{"allocated-subnets": subnetsStr(allocatedSubnets)}) 80 if network.Subnet, err = sn.SelectSubnet(p.dynamicRange, allocatedSubnets); err != nil { 81 logger.Error("subnet-selecting-failed", err) 82 return nil, err 83 } 84 logger.Info("subnet-selected", lager.Data{"subnet": network.Subnet.String()}) 85 86 ips := p.allocated[network.Subnet.String()] 87 logger.Info("ip-selecting", lager.Data{"allocated-ips": ipsStr(ips)}) 88 allocatedIPs := append(ips, NetworkIP(network.Subnet), GatewayIP(network.Subnet), BroadcastIP(network.Subnet)) 89 if network.IP, err = i.SelectIP(network.Subnet, allocatedIPs); err != nil { 90 logger.Error("ip-selecting-failed", err) 91 return nil, err 92 } 93 logger.Info("ip-selected", lager.Data{"ip": network.IP.String()}) 94 95 p.allocated[network.Subnet.String()] = append(ips, network.IP) 96 logger.Info("new-allocated", lager.Data{"allocated-ips": ipsStr(p.allocated[network.Subnet.String()])}) 97 98 return network, nil 99 } 100 101 // Remove re-allocates a given subnet and ip address combination in the pool. It returns 102 // an error if the combination is already allocated. 103 func (p *pool) Remove(network *linux_backend.Network, logger lager.Logger) error { 104 p.mu.Lock() 105 defer p.mu.Unlock() 106 107 logger = logger.Session("release") 108 109 if network.IP == nil { 110 return ErrIpCannotBeNil 111 } 112 113 for _, existing := range p.allocated[network.Subnet.String()] { 114 if existing.Equal(network.IP) { 115 return ErrOverlapsExistingSubnet 116 } 117 } 118 119 p.allocated[network.Subnet.String()] = append(p.allocated[network.Subnet.String()], network.IP) 120 logger.Info("new-allocated", lager.Data{"allocated-ips": ipsStr(p.allocated[network.Subnet.String()]), "subnet": network.Subnet.String()}) 121 122 return nil 123 } 124 125 func (p *pool) Release(network *linux_backend.Network, logger lager.Logger) error { 126 p.mu.Lock() 127 defer p.mu.Unlock() 128 129 logger = logger.Session("release") 130 131 subnetString := network.Subnet.String() 132 ips := p.allocated[subnetString] 133 134 if i, found := indexOf(ips, network.IP); found { 135 if reducedIps, empty := removeIPAtIndex(ips, i); empty { 136 delete(p.allocated, subnetString) 137 logger.Info("changing-allocated-subnets", lager.Data{"allocated-subnets": subnetsStr(subnets(p.allocated))}) 138 } else { 139 p.allocated[subnetString] = reducedIps 140 logger.Info("changing-allocated-ips", lager.Data{"allocated-ips": ipsStr(reducedIps)}) 141 } 142 143 return nil 144 } 145 146 return ErrReleasedUnallocatedSubnet 147 } 148 149 // Capacity returns the number of /30 subnets that can be allocated 150 // from the pool's dynamic allocation range. 151 func (m *pool) Capacity() int { 152 masked, total := m.dynamicRange.Mask.Size() 153 return int(math.Pow(2, float64(total-masked)) / 4) 154 } 155 156 // Returns the gateway IP of a given subnet, which is always the maximum valid IP 157 func GatewayIP(subnet *net.IPNet) net.IP { 158 return next(subnet.IP) 159 } 160 161 // Returns the network IP of a subnet. 162 func NetworkIP(subnet *net.IPNet) net.IP { 163 return subnet.IP 164 } 165 166 // Returns the broadcast IP of a subnet. 167 func BroadcastIP(subnet *net.IPNet) net.IP { 168 return max(subnet) 169 } 170 171 // returns the keys in the given map whose values are non-empty slices 172 func subnets(m map[string][]net.IP) (result []*net.IPNet) { 173 for k, v := range m { 174 if len(v) > 0 { 175 _, ipn, err := net.ParseCIDR(k) 176 if err != nil { 177 panic(fmt.Sprintf("failed to parse a CIDR in the subnet pool: %s", err)) 178 } 179 180 result = append(result, ipn) 181 } 182 } 183 184 return result 185 } 186 187 func subnetsStr(subnets []*net.IPNet) []string { 188 var retVal []string 189 190 for _, subnet := range subnets { 191 retVal = append(retVal, subnet.String()) 192 } 193 194 return retVal 195 } 196 197 func ipsStr(ips []net.IP) []string { 198 var retVal []string 199 200 for _, ip := range ips { 201 retVal = append(retVal, ip.String()) 202 } 203 204 return retVal 205 } 206 207 func indexOf(a []net.IP, w net.IP) (int, bool) { 208 for i, v := range a { 209 if v.Equal(w) { 210 return i, true 211 } 212 } 213 214 return -1, false 215 } 216 217 // removeAtIndex removes from a slice at the given index, 218 // and returns the new slice and boolean, true iff the new slice is empty. 219 func removeIPAtIndex(ips []net.IP, i int) ([]net.IP, bool) { 220 l := len(ips) 221 ips[i] = ips[l-1] 222 ips = ips[:l-1] 223 return ips, l == 1 224 }