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