Skip to content
Snippets Groups Projects
bgp_path.go 6.23 KiB
Newer Older
Oliver Herms's avatar
Oliver Herms committed
import (
	"crypto/sha256"
Oliver Herms's avatar
Oliver Herms committed
	"fmt"
Oliver Herms's avatar
Oliver Herms committed

Oliver Herms's avatar
Oliver Herms committed
	"github.com/bio-routing/bio-rd/protocols/bgp/types"
Oliver Herms's avatar
Oliver Herms committed
	"github.com/taktv6/tflow2/convert"
)

// BGPPath represents a set of BGP path attributes
type BGPPath struct {
	PathIdentifier    uint32
	NextHop           uint32
	LocalPref         uint32
Oliver Herms's avatar
Oliver Herms committed
	ASPath            types.ASPath
	ASPathLen         uint16
	Origin            uint8
	MED               uint32
	EBGP              bool
	AtomicAggregate   bool
	Aggregator        types.Aggregator
	BGPIdentifier     uint32
	Source            uint32
	Communities       []uint32
Oliver Herms's avatar
Oliver Herms committed
	LargeCommunities  []types.LargeCommunity
	UnknownAttributes []types.UnknownPathAttribute
// Length get's the length of serialized path
func (b *BGPPath) Length() uint16 {
Oliver Herms's avatar
Oliver Herms committed
	asPathLen := uint16(3)
	for _, segment := range b.ASPath {
		asPathLen++
		asPathLen += uint16(4 * len(segment.ASNs))
	}

Oliver Herms's avatar
Oliver Herms committed
	communitiesLen := uint16(0)
	if len(b.Communities) != 0 {
		communitiesLen += 3 + uint16(len(b.Communities)*4)
	}

	largeCommunitiesLen := uint16(0)
	if len(b.LargeCommunities) != 0 {
		largeCommunitiesLen += 3 + uint16(len(b.LargeCommunities)*12)
	}

Oliver Herms's avatar
Oliver Herms committed
	unknownAttributesLen := uint16(0)
	for _, unknownAttr := range b.UnknownAttributes {
		unknownAttributesLen += unknownAttr.WireLength()
Oliver Herms's avatar
Oliver Herms committed
	return communitiesLen + largeCommunitiesLen + 4*7 + 4 + asPathLen + unknownAttributesLen
Oliver Herms's avatar
Oliver Herms committed
// ECMP determines if routes b and c are euqal in terms of ECMP
func (b *BGPPath) ECMP(c *BGPPath) bool {
	return b.LocalPref == c.LocalPref && b.ASPathLen == c.ASPathLen && b.MED == c.MED && b.Origin == c.Origin
}
Oliver Herms's avatar
Oliver Herms committed
// Compare returns negative if b < c, 0 if paths are equal, positive if b > c
func (b *BGPPath) Compare(c *BGPPath) int8 {
	if c.LocalPref < b.LocalPref {
		return 1
	}
Oliver Herms's avatar
Oliver Herms committed
	if c.LocalPref > b.LocalPref {
		return -1
	}
	// 9.1.2.2.  Breaking Ties (Phase 2)
Oliver Herms's avatar
Oliver Herms committed
	if c.ASPathLen > b.ASPathLen {
		return 1
	}
Oliver Herms's avatar
Oliver Herms committed
	if c.ASPathLen < b.ASPathLen {
		return -1
	}
Oliver Herms's avatar
Oliver Herms committed
	if c.Origin > b.Origin {
		return 1
	}

	if c.Origin < b.Origin {
		return -1
Oliver Herms's avatar
Oliver Herms committed
	if c.MED > b.MED {
		return 1
	}

	if c.MED < b.MED {
		return -1
	}

	// d)
	if c.EBGP && !b.EBGP {
		return -1
	}

	if !c.EBGP && b.EBGP {
		return 1
	}

	// e) TODO: interiour cost (hello IS-IS and OSPF)

	// f)
Oliver Herms's avatar
Oliver Herms committed
	if c.BGPIdentifier < b.BGPIdentifier {
		return 1
	}

	if c.BGPIdentifier > b.BGPIdentifier {
		return -1
	}

Oliver Herms's avatar
Oliver Herms committed
	if c.Source < b.Source {
		return 1
	}

	if c.Source > b.Source {
		return -1
	}

	if c.NextHop < b.NextHop {
		return 1
	}

	if c.NextHop > b.NextHop {
		return -1
	}

Oliver Herms's avatar
Oliver Herms committed
	return 0
}

func (b *BGPPath) betterECMP(c *BGPPath) bool {
	if c.LocalPref < b.LocalPref {
		return false
	}

	if c.LocalPref > b.LocalPref {
		return true
	}

	if c.ASPathLen > b.ASPathLen {
		return false
	}

	if c.ASPathLen < b.ASPathLen {
		return true
	}

	if c.Origin > b.Origin {
		return false
	}

	if c.Origin < b.Origin {
		return true
	}

	if c.MED > b.MED {
		return false
	}

	if c.MED < b.MED {
		return true
	}

	return false
}

func (b *BGPPath) better(c *BGPPath) bool {
	if b.betterECMP(c) {
		return true
	}

	if c.BGPIdentifier < b.BGPIdentifier {
		return true
	}

	if c.Source < b.Source {
		return true
	}

	return false
}

// Print all known information about a route in human readable form
Oliver Herms's avatar
Oliver Herms committed
func (b *BGPPath) Print() string {
	origin := ""
	switch b.Origin {
	case 0:
		origin = "Incomplete"
	case 1:
		origin = "EGP"
	case 2:
		origin = "IGP"
	}
Oliver Herms's avatar
Oliver Herms committed
	ret := fmt.Sprintf("\t\tLocal Pref: %d\n", b.LocalPref)
	ret += fmt.Sprintf("\t\tOrigin: %s\n", origin)
	ret += fmt.Sprintf("\t\tAS Path: %v\n", b.ASPath)
	ret += fmt.Sprintf("\t\tBGP type: %s\n", bgpType)
Oliver Herms's avatar
Oliver Herms committed
	nh := uint32To4Byte(b.NextHop)
	ret += fmt.Sprintf("\t\tNEXT HOP: %d.%d.%d.%d\n", nh[0], nh[1], nh[2], nh[3])
	ret += fmt.Sprintf("\t\tMED: %d\n", b.MED)
Oliver Herms's avatar
Oliver Herms committed
	ret += fmt.Sprintf("\t\tPath ID: %d\n", b.PathIdentifier)
	src := uint32To4Byte(b.Source)
	ret += fmt.Sprintf("\t\tSource: %d.%d.%d.%d\n", src[0], src[1], src[2], src[3])
	ret += fmt.Sprintf("\t\tCommunities: %v\n", b.Communities)
	ret += fmt.Sprintf("\t\tLargeCommunities: %v\n", b.LargeCommunities)
Oliver Herms's avatar
Oliver Herms committed

	return ret
}

// Prepend the given BGPPath with the given ASN given times
func (b *BGPPath) Prepend(asn uint32, times uint16) {
	if len(b.ASPath) == 0 {
		b.insertNewASSequence()
	}

Daniel Czerwonk's avatar
Daniel Czerwonk committed
	first := b.ASPath[0]
Oliver Herms's avatar
Oliver Herms committed
	if first.Type == types.ASSet {
Daniel Czerwonk's avatar
Daniel Czerwonk committed
		b.insertNewASSequence()
	}

	for i := 0; i < int(times); i++ {
Oliver Herms's avatar
Oliver Herms committed
		if len(b.ASPath) == types.MaxASNsSegment {
Daniel Czerwonk's avatar
Daniel Czerwonk committed
			b.insertNewASSequence()
		}

		old := b.ASPath[0].ASNs
		asns := make([]uint32, len(old)+1)
		copy(asns[1:], old)
		asns[0] = asn
		b.ASPath[0].ASNs = asns
Daniel Czerwonk's avatar
Daniel Czerwonk committed
	}

	b.ASPathLen = b.ASPath.Length()
}
func (b *BGPPath) insertNewASSequence() {
Oliver Herms's avatar
Oliver Herms committed
	pa := make(types.ASPath, len(b.ASPath)+1)
Daniel Czerwonk's avatar
Daniel Czerwonk committed
	copy(pa[1:], b.ASPath)
Oliver Herms's avatar
Oliver Herms committed
	pa[0] = types.ASPathSegment{
Daniel Czerwonk's avatar
Daniel Czerwonk committed
		ASNs: make([]uint32, 0),
		Type: types.ASSequence,
Oliver Herms's avatar
Oliver Herms committed
// Copy creates a deep copy of a BGPPath
func (b *BGPPath) Copy() *BGPPath {
	if b == nil {
Daniel Czerwonk's avatar
Daniel Czerwonk committed
		return nil
	}

Oliver Herms's avatar
Oliver Herms committed
	cp := *b

	cp.ASPath = make(types.ASPath, len(cp.ASPath))
	copy(cp.ASPath, b.ASPath)

	cp.Communities = make([]uint32, len(cp.Communities))
	copy(cp.Communities, b.Communities)

	cp.LargeCommunities = make([]types.LargeCommunity, len(cp.LargeCommunities))
	copy(cp.LargeCommunities, b.LargeCommunities)

Daniel Czerwonk's avatar
Daniel Czerwonk committed
	return &cp
}

// ComputeHash computes an hash over all attributes of the path
func (b *BGPPath) ComputeHash() string {
	s := fmt.Sprintf("%d\t%d\t%v\t%d\t%d\t%v\t%d\t%d\t%v\t%v\t%d",
		b.NextHop,
		b.LocalPref,
		b.ASPath,
		b.Origin,
		b.MED,
		b.EBGP,
		b.BGPIdentifier,
		b.Source,
		b.Communities,
		b.LargeCommunities,
		b.PathIdentifier)

Daniel Czerwonk's avatar
Daniel Czerwonk committed
	return fmt.Sprintf("%x", sha256.Sum256([]byte(s)))
// CommunitiesString returns the formated communities
func (b *BGPPath) CommunitiesString() string {
	str := ""
	for _, com := range b.Communities {
Oliver Herms's avatar
Oliver Herms committed
		str += types.CommunityStringForUint32(com) + " "
	}

	return strings.TrimRight(str, " ")
}

// LargeCommunitiesString returns the formated communities
func (b *BGPPath) LargeCommunitiesString() string {
	str := ""
	for _, com := range b.LargeCommunities {
		str += com.String() + " "
	}

	return strings.TrimRight(str, " ")
}

Oliver Herms's avatar
Oliver Herms committed
func uint32To4Byte(addr uint32) [4]byte {
	slice := convert.Uint32Byte(addr)
	ret := [4]byte{
		slice[0],
		slice[1],
		slice[2],
		slice[3],
	}
	return ret
}