kilo/pkg/mesh/ip.go

114 lines
2.5 KiB
Go

// Copyright 2019 the Kilo authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package mesh
import (
"net"
"sort"
)
// sortIPs sorts IPs so the result is stable.
// It will first sort IPs by type, to prefer selecting
// IPs of the same type, and then by value.
func sortIPs(ips []*net.IPNet) {
sort.Slice(ips, func(i, j int) bool {
i4, j4 := ips[i].IP.To4(), ips[j].IP.To4()
if i4 != nil && j4 == nil {
return true
}
if j4 != nil && i4 == nil {
return false
}
return ips[i].String() < ips[j].String()
})
}
func isLocal(ip net.IP) bool {
return ip.IsLoopback() || ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast()
}
func isPublic(ip net.IP) bool {
// Check RFC 1918 addresses.
if ip4 := ip.To4(); ip4 != nil {
switch {
// Check for 10.0.0.0/8.
case ip4[0] == 10:
return false
// Check for 172.16.0.0/12.
case ip4[0] == 172 && ip4[1]&0xf0 == 0x10:
return false
// Check for 192.168.0.0/16.
case ip4[0] == 192 && ip4[1] == 168:
return false
default:
return true
}
}
// Check RFC 4193 addresses.
if len(ip) == net.IPv6len {
switch {
// Check for fd00::/8.
case ip[0] == 0xfd && ip[1] == 0x00:
return false
default:
return true
}
}
return false
}
type allocator struct {
bits int
ones int
cidr *net.IPNet
current net.IP
}
func newAllocator(cidr net.IPNet) *allocator {
ones, bits := cidr.Mask.Size()
current := make(net.IP, len(cidr.IP))
copy(current, cidr.IP)
if ip4 := current.To4(); ip4 != nil {
current = ip4
}
return &allocator{
bits: bits,
ones: ones,
cidr: &cidr,
current: current,
}
}
func (a *allocator) next() *net.IPNet {
if a.current == nil {
return nil
}
for i := len(a.current) - 1; i >= 0; i-- {
a.current[i]++
// if we haven't overflowed, then we can exit.
if a.current[i] != 0 {
break
}
}
if !a.cidr.Contains(a.current) {
a.current = nil
}
ip := make(net.IP, len(a.current))
copy(ip, a.current)
return &net.IPNet{IP: ip, Mask: net.CIDRMask(a.ones, a.bits)}
}