Skip to content
Snippets Groups Projects
prefix.go 5.04 KiB
Newer Older
Oliver Herms's avatar
Oliver Herms committed
package net

import (
	"fmt"
	"math"
cedi's avatar
cedi committed
	gonet "net"
Oliver Herms's avatar
Oliver Herms committed
	"strconv"
	"strings"
Oliver Herms's avatar
Oliver Herms committed

	"github.com/bio-routing/bio-rd/net/api"
Julian Kornberger's avatar
Julian Kornberger committed
	"github.com/pkg/errors"
Oliver Herms's avatar
Oliver Herms committed
)

// Prefix represents an IPv4 prefix
type Prefix struct {
Oliver Herms's avatar
Oliver Herms committed
	pfxlen uint8
}

// Dedup gets a copy of Prefix from the cache
func (p *Prefix) Dedup() *Prefix {
	return pfxc.get(p)
}

// DedupWithIP gets a copy of Prefix from the cache and dedups the IP part
func (p *Prefix) DedupWithIP() *Prefix {
	p.addr = p.addr.Dedup()
	return pfxc.get(p)
}

Oliver Herms's avatar
Oliver Herms committed
// NewPrefixFromProtoPrefix creates a Prefix from a proto Prefix
func NewPrefixFromProtoPrefix(pfx api.Prefix) *Prefix {
	return &Prefix{
Oliver Herms's avatar
Oliver Herms committed
		addr:   IPFromProtoIP(*pfx.Address),
		pfxlen: uint8(pfx.Pfxlen),
	}
}

// PrefixFromString converts prefix from string representation to Prefix
func PrefixFromString(s string) (*Prefix, error) {
	parts := strings.Split(s, "/")
	if len(parts) != 2 {
		return nil, fmt.Errorf("Invalid format: %q", s)
	}

	ip, err := IPFromString(parts[0])
	if err != nil {
	}

	l, err := strconv.Atoi(parts[1])
	if err != nil {
		return nil, errors.Wrap(err, "Unable to convert to int")
		addr:   ip,
		pfxlen: uint8(l),
	}, nil
}

Oliver Herms's avatar
Oliver Herms committed
// ToProto converts prefix to proto prefix
func (p *Prefix) ToProto() *api.Prefix {
	return &api.Prefix{
		Address: p.addr.ToProto(),
		Pfxlen:  uint32(p.pfxlen),
Oliver Herms's avatar
Oliver Herms committed
	}
}

Oliver Herms's avatar
Oliver Herms committed
// NewPfx creates a new Prefix
func NewPfx(addr *IP, pfxlen uint8) *Prefix {
	return &Prefix{
Oliver Herms's avatar
Oliver Herms committed
		addr:   addr,
		pfxlen: pfxlen,
	}
}

cedi's avatar
cedi committed
// NewPfxFromIPNet creates a Prefix object from an gonet.IPNet object
func NewPfxFromIPNet(ipNet *gonet.IPNet) *Prefix {
cedi's avatar
cedi committed
	ones, _ := ipNet.Mask.Size()
	ip, _ := IPFromBytes(ipNet.IP)

cedi's avatar
cedi committed
		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 {
Julian Kornberger's avatar
Julian Kornberger committed
			return 0, errors.Wrapf(err, "Unable to convert %q to int", parts[i])
Oliver Herms's avatar
Oliver Herms committed
		}

		if y > 255 {
			return 0, fmt.Errorf("%d is too big for a uint8", y)
		}

Julian Kornberger's avatar
Julian Kornberger committed
		ret += uint32(y) << uint((3-i)*8)
Oliver Herms's avatar
Oliver Herms committed
	}

	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 gonet.IP object for a Prefix object
func (pfx *Prefix) GetIPNet() *gonet.IPNet {
cedi's avatar
cedi committed
	var dstNetwork gonet.IPNet
cedi's avatar
cedi committed
	dstNetwork.IP = pfx.Addr().Bytes()

	pfxLen := int(pfx.Pfxlen())
	if pfx.Addr().IsIPv4() {
cedi's avatar
cedi committed
		dstNetwork.Mask = gonet.CIDRMask(pfxLen, 32)
cedi's avatar
cedi committed
	} else {
cedi's avatar
cedi committed
		dstNetwork.Mask = gonet.CIDRMask(pfxLen, 128)
cedi's avatar
cedi committed
	}

	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
	}

Oliver Herms's avatar
Oliver Herms committed
	if pfx.addr.isLegacy {
		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 {
Oliver Herms's avatar
Oliver Herms committed
	if pfx.addr.isLegacy {
		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
}