Skip to content
Snippets Groups Projects
bgp_path.go 6.29 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

	bnet "github.com/bio-routing/bio-rd/net"
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           bnet.IP
	LocalPref         uint32
Oliver Herms's avatar
Oliver Herms committed
	ASPath            types.ASPath
	ASPathLen         uint16
	Origin            uint8
	MED               uint32
	EBGP              bool
	BGPIdentifier     uint32
Daniel Czerwonk's avatar
Daniel Czerwonk committed
	Source            bnet.IP
	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
	}

Daniel Czerwonk's avatar
Daniel Czerwonk committed
	if c.Source.Compare(b.Source) == -1 {
Oliver Herms's avatar
Oliver Herms committed
		return 1
	}

Daniel Czerwonk's avatar
Daniel Czerwonk committed
	if c.Source.Compare(b.Source) == 1 {
Oliver Herms's avatar
Oliver Herms committed
		return -1
	}

Daniel Czerwonk's avatar
Daniel Czerwonk committed
	if c.NextHop.Compare(b.NextHop) == -1 {
Daniel Czerwonk's avatar
Daniel Czerwonk committed
	if c.NextHop.Compare(b.NextHop) == 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
	}

Daniel Czerwonk's avatar
Daniel Czerwonk committed
	if c.Source.Compare(b.Source) == -1 {
// 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)
Daniel Czerwonk's avatar
Daniel Czerwonk committed
	nh := uint32To4Byte(b.NextHop.ToUint32())
Oliver Herms's avatar
Oliver Herms committed
	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
}