github.com/dahs81/otto@v0.2.1-0.20160126165905-6400716cf085/helper/localaddr/subnet.go (about)

     1  // localaddr is a helper library for allocating local IP addresses.
     2  //
     3  // localaddr does its best to ensure that the IP addresses allocated are
     4  // both predictable (if possible) and on a subnet that isn't in use.
     5  package localaddr
     6  
     7  import (
     8  	"fmt"
     9  	"log"
    10  	"net"
    11  )
    12  
    13  // RFC 1918
    14  var privateCIDR = []string{"172.16.0.0/12", "10.0.0.0/8", "192.168.0.0/16"}
    15  var privateIPNets = make([]*net.IPNet, len(privateCIDR))
    16  
    17  func init() {
    18  	for i, cidr := range privateCIDR {
    19  		_, ipnet, err := net.ParseCIDR(cidr)
    20  		if err != nil {
    21  			panic(err)
    22  		}
    23  
    24  		privateIPNets[i] = ipnet
    25  	}
    26  }
    27  
    28  // UsableSubnet returns a /24 CIDR block of usable network addresses in the
    29  // RFC private address space that also isn't in use by any network interface
    30  // on this machine currently.
    31  func UsableSubnet() (*net.IPNet, error) {
    32  	interfaces, err := net.Interfaces()
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  
    37  	// First find all the taken private IP spaces
    38  	taken := make([]*net.IPNet, 0, 5)
    39  	for _, i := range interfaces {
    40  		addrs, err := i.Addrs()
    41  		if err != nil {
    42  			return nil, err
    43  		}
    44  
    45  		for _, addr := range addrs {
    46  			if addr.Network() != "ip+net" {
    47  				log.Printf("[DEBUG] ignoring non ip+net addr: %s", addr)
    48  				continue
    49  			}
    50  
    51  			// Parse the CIDR block of this interface
    52  			ip, ipnet, err := net.ParseCIDR(addr.String())
    53  			if err != nil {
    54  				return nil, err
    55  			}
    56  
    57  			// We only do IPv4 for now
    58  			if ip.To4() == nil {
    59  				log.Printf("[DEBUG] ignoring non IPv4 addr: %s", addr)
    60  				continue
    61  			}
    62  
    63  			// If the addr isn't even in the private IP space, then ignore it
    64  			private := false
    65  			for _, ipnet := range privateIPNets {
    66  				if ipnet.Contains(ip) {
    67  					private = true
    68  					break
    69  				}
    70  			}
    71  			if !private {
    72  				log.Printf("[DEBUG] ignoring non-private IP space: %s", addr)
    73  				continue
    74  			}
    75  
    76  			log.Printf("[DEBUG] occupied private IP space: %s", addr)
    77  			taken = append(taken, ipnet)
    78  		}
    79  	}
    80  
    81  	// Now go through and find a space that we can use
    82  	for _, ipnet := range privateIPNets {
    83  		// Get the first IP, and add one since we always want to start at
    84  		// x.x.1.x since x.x.0.x sometimes means something special.
    85  		ip := ipnet.IP.Mask(ipnet.Mask)
    86  		for ip[2] = 1; ip[2] <= 255; ip[2]++ {
    87  			// Determine if one of our taken CIDRs has this IP. We can
    88  			// probably do this way more efficiently but this is fine for now.
    89  			bad := false
    90  			for _, ipnet := range taken {
    91  				if ipnet.Contains(ip) {
    92  					bad = true
    93  					break
    94  				}
    95  			}
    96  			if bad {
    97  				continue
    98  			}
    99  
   100  			// This is a good address space
   101  			_, ipnet, err := net.ParseCIDR(fmt.Sprintf("%s/24", ip))
   102  			if err != nil {
   103  				return nil, err
   104  			}
   105  
   106  			log.Printf("[DEBUG] found usable subnet: %s", ipnet)
   107  			return ipnet, nil
   108  		}
   109  	}
   110  
   111  	return nil, fmt.Errorf("no usable subnet found in private space")
   112  }