Skip to content
Snippets Groups Projects
prefix.go 3.85 KiB
Newer Older
  • Learn to ignore specific revisions
  • Oliver Herms's avatar
    Oliver Herms committed
    package net
    
    import (
    	"fmt"
    	"math"
    
    cedi's avatar
    cedi committed
    	"net"
    
    Oliver Herms's avatar
    Oliver Herms committed
    	"strconv"
    	"strings"
    )
    
    // Prefix represents an IPv4 prefix
    type Prefix struct {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	pfxlen uint8
    }
    
    // NewPfx creates a new Prefix
    
    func NewPfx(addr IP, pfxlen uint8) Prefix {
    
    Oliver Herms's avatar
    Oliver Herms committed
    		addr:   addr,
    		pfxlen: pfxlen,
    	}
    }
    
    
    cedi's avatar
    cedi committed
    // NewPfxFromIPNet creates a Prefix object from an net.IPNet object
    
    cedi's avatar
    cedi committed
    func NewPfxFromIPNet(ipNet *net.IPNet) Prefix {
    	ones, _ := ipNet.Mask.Size()
    	ip, _ := IPFromBytes(ipNet.IP)
    
    	return Prefix{
    		addr:   ip,
    		pfxlen: uint8(ones),
    	}
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    // StrToAddr converts an IP address string to it's uint32 representation
    func StrToAddr(x string) (uint32, error) {
    	parts := strings.Split(x, ".")
    	if len(parts) != 4 {
    		return 0, fmt.Errorf("Invalid format")
    	}
    
    	ret := uint32(0)
    	for i := 0; i < 4; i++ {
    		y, err := strconv.Atoi(parts[i])
    		if err != nil {
    			return 0, fmt.Errorf("Unable to convert %q to int: %v", parts[i], err)
    		}
    
    		if y > 255 {
    			return 0, fmt.Errorf("%d is too big for a uint8", y)
    		}
    
    		ret += uint32(math.Pow(256, float64(3-i))) * uint32(y)
    	}
    
    	return ret, nil
    }
    
    // Addr returns the address of the prefix
    
    func (pfx Prefix) Addr() IP {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	return pfx.addr
    }
    
    // Pfxlen returns the length of the prefix
    
    func (pfx Prefix) Pfxlen() uint8 {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	return pfx.pfxlen
    }
    
    // String returns a string representation of pfx
    
    func (pfx Prefix) String() string {
    
    	return fmt.Sprintf("%s/%d", pfx.addr, pfx.pfxlen)
    
    Oliver Herms's avatar
    Oliver Herms committed
    }
    
    
    cedi's avatar
    cedi committed
    // GetIPNet returns the net.IP object for a Prefix object
    
    cedi's avatar
    cedi committed
    func (pfx Prefix) GetIPNet() *net.IPNet {
    	var dstNetwork net.IPNet
    	dstNetwork.IP = pfx.Addr().Bytes()
    
    	pfxLen := int(pfx.Pfxlen())
    	if pfx.Addr().IsIPv4() {
    		dstNetwork.Mask = net.CIDRMask(pfxLen, 32)
    	} else {
    		dstNetwork.Mask = net.CIDRMask(pfxLen, 128)
    	}
    
    	return &dstNetwork
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    // Contains checks if x is a subnet of or equal to pfx
    
    func (pfx Prefix) Contains(x Prefix) bool {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	if x.pfxlen <= pfx.pfxlen {
    		return false
    	}
    
    
    	if pfx.addr.ipVersion == 4 {
    		return pfx.containsIPv4(x)
    	}
    
    
    	return pfx.containsIPv6(x)
    
    }
    
    func (pfx Prefix) containsIPv4(x Prefix) bool {
    
    	mask := uint32((math.MaxUint32 << (32 - pfx.pfxlen)))
    
    	return (pfx.addr.ToUint32() & mask) == (x.addr.ToUint32() & mask)
    
    Oliver Herms's avatar
    Oliver Herms committed
    }
    
    
    func (pfx Prefix) containsIPv6(x Prefix) bool {
    	var maskHigh, maskLow uint64
    	if pfx.pfxlen <= 64 {
    		maskHigh = math.MaxUint32 << (64 - pfx.pfxlen)
    		maskLow = uint64(0)
    	} else {
    		maskHigh = math.MaxUint32
    		maskLow = math.MaxUint32 << (128 - pfx.pfxlen)
    	}
    
    	return pfx.addr.higher&maskHigh&maskHigh == x.addr.higher&maskHigh&maskHigh &&
    		pfx.addr.lower&maskHigh&maskLow == x.addr.lower&maskHigh&maskLow
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    // Equal checks if pfx and x are equal
    
    func (pfx Prefix) Equal(x Prefix) bool {
    
    cedi's avatar
    cedi committed
    	return pfx.addr.Equal(x.addr) && pfx.pfxlen == x.pfxlen
    
    Oliver Herms's avatar
    Oliver Herms committed
    }
    
    // GetSupernet gets the next common supernet of pfx and x
    
    func (pfx Prefix) GetSupernet(x Prefix) Prefix {
    
    	if pfx.addr.ipVersion == 4 {
    		return pfx.supernetIPv4(x)
    	}
    
    
    	return pfx.supernetIPv6(x)
    
    }
    
    func (pfx Prefix) supernetIPv4(x Prefix) Prefix {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	maxPfxLen := min(pfx.pfxlen, x.pfxlen) - 1
    
    	a := pfx.addr.ToUint32() >> (32 - maxPfxLen)
    	b := x.addr.ToUint32() >> (32 - maxPfxLen)
    
    Oliver Herms's avatar
    Oliver Herms committed
    
    	for i := 0; a != b; i++ {
    		a = a >> 1
    		b = b >> 1
    		maxPfxLen--
    	}
    
    
    		addr:   IPv4(a << (32 - maxPfxLen)),
    
    Oliver Herms's avatar
    Oliver Herms committed
    		pfxlen: maxPfxLen,
    	}
    }
    
    
    func (pfx Prefix) supernetIPv6(x Prefix) Prefix {
    	maxPfxLen := min(pfx.pfxlen, x.pfxlen)
    
    	a := pfx.addr.BitAtPosition(1)
    	b := x.addr.BitAtPosition(1)
    	pfxLen := uint8(0)
    	mask := uint64(0)
    	for a == b && pfxLen < maxPfxLen {
    		a = pfx.addr.BitAtPosition(pfxLen + 2)
    		b = x.addr.BitAtPosition(pfxLen + 2)
    		pfxLen++
    
    		if pfxLen == 64 {
    			mask = 0
    		}
    
    		m := pfxLen % 64
    		mask = mask + uint64(1)<<(64-m)
    	}
    
    	if pfxLen == 0 {
    		return NewPfx(IPv6(0, 0), pfxLen)
    	}
    
    	if pfxLen > 64 {
    		return NewPfx(IPv6(pfx.addr.higher, pfx.addr.lower&mask), pfxLen)
    	}
    
    	return NewPfx(IPv6(pfx.addr.higher&mask, 0), pfxLen)
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func min(a uint8, b uint8) uint8 {
    	if a < b {
    		return a
    	}
    	return b
    }