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  }