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 }