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

	"github.com/taktv6/tflow2/convert"

	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
)

// 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
	AtomicAggregate   bool
	Aggregator        *types.Aggregator
	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)
	}

	clusterListLen := uint16(0)
	if len(b.ClusterList) != 0 {
		clusterListLen += 3 + uint16(len(b.ClusterList)*4)
	}

Oliver Herms's avatar
Oliver Herms committed
	unknownAttributesLen := uint16(0)
	for _, unknownAttr := range b.UnknownAttributes {
		unknownAttributesLen += unknownAttr.WireLength()
	originatorID := uint16(0)
	if b.OriginatorID != 0 {
		originatorID = 4
	}

	return communitiesLen + largeCommunitiesLen + 4*7 + 4 + originatorID + 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
}
// Equal checks if paths are equal
func (b *BGPPath) Equal(c *BGPPath) bool {
	if b.PathIdentifier != c.PathIdentifier {
		return false
	}

	return b.Select(c) == 0
}

// Select returns negative if b < c, 0 if paths are equal, positive if b > c
func (b *BGPPath) Select(c *BGPPath) int8 {
Oliver Herms's avatar
Oliver Herms committed
	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: interior cost (hello IS-IS and OSPF)
	// f) + RFC4456 9. (Route Reflection)
	bgpIdentifierC := c.BGPIdentifier
	bgpIdentifierB := b.BGPIdentifier

	// IF an OriginatorID (set by an RR) is present, use this instead of Originator
	if c.OriginatorID != 0 {
		bgpIdentifierC = c.OriginatorID
	}

	if b.OriginatorID != 0 {
		bgpIdentifierB = b.OriginatorID
	}

	if bgpIdentifierC < bgpIdentifierB {
Oliver Herms's avatar
Oliver Herms committed
		return 1
	}

	if bgpIdentifierC > bgpIdentifierB {
		return -1
	}

	// Additionally check for the shorter ClusterList
	if len(c.ClusterList) < len(b.ClusterList) {
Oliver Herms's avatar
Oliver Herms committed
		return 1
	}

	if len(c.ClusterList) > len(b.ClusterList) {
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.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)
	ret += fmt.Sprintf("\t\tNEXT HOP: %s\n", b.NextHop)
Oliver Herms's avatar
Oliver Herms committed
	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)
	ret += fmt.Sprintf("\t\tSource: %s\n", b.Source)
	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

		oid := convert.Uint32Byte(b.OriginatorID)
		ret += fmt.Sprintf("\t\tOriginatorID: %d.%d.%d.%d\n", oid[0], oid[1], oid[2], oid[3])
	}
	if b.ClusterList != nil {
		ret += fmt.Sprintf("\t\tClusterList %s\n", b.ClusterListString())
	}

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)

	if b.ClusterList != nil {
		cp.ClusterList = make([]uint32, len(cp.ClusterList))
		copy(cp.ClusterList, b.ClusterList)
	}

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("%s\t%d\t%v\t%d\t%d\t%v\t%d\t%s\t%v\t%v\t%d\t%d\t%v",
		b.NextHop,
		b.LocalPref,
		b.ASPath,
		b.Origin,
		b.MED,
		b.EBGP,
		b.BGPIdentifier,
		b.Source,
		b.Communities,
		b.LargeCommunities,
		b.PathIdentifier,
		b.OriginatorID,
		b.ClusterList)
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, " ")
}

// ClusterListString returns the formated ClusterList
func (b *BGPPath) ClusterListString() string {
	str := ""
	for _, cid := range b.ClusterList {
		octes := convert.Uint32Byte(cid)
		str += fmt.Sprintf("%d.%d.%d.%d ", octes[0], octes[1], octes[2], octes[3])
	}

	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, " ")
}