diff --git a/route/bgp.go b/route/bgp.go index a63eb2fd396f16b2ae61e6f24d863ea5fbace185..80aebdfe2dbb01b6b54a90a2fec6cd77b4e2ced4 100644 --- a/route/bgp.go +++ b/route/bgp.go @@ -1,6 +1,7 @@ package route import ( + "crypto/sha256" "fmt" "strconv" "strings" @@ -192,6 +193,28 @@ func (p *BGPPath) Copy() *BGPPath { return &cp } +// ComputeHash computes an hash over all attributes of the path +func (b *BGPPath) ComputeHash() string { + s := fmt.Sprintf("%d\t%d\t%s\t%d\t%d\t%v\t%d\t%d\t%s\t%s\t%d", + b.NextHop, + b.LocalPref, + b.ASPath, + b.Origin, + b.MED, + b.EBGP, + b.BGPIdentifier, + b.Source, + b.Communities, + b.LargeCommunities, + b.PathIdentifier) + + r := strings.NewReader(s) + h := sha256.New() + r.WriteTo(h) + + return fmt.Sprintf("%x", h.Sum(nil)) +} + func uint32To4Byte(addr uint32) [4]byte { slice := convert.Uint32Byte(addr) ret := [4]byte{ diff --git a/route/bgp_path_manager.go b/route/bgp_path_manager.go index f846d3b60e108fc4be97597fabefe4ab46cb9d13..0eec9427911eced59371ea3ebf44a91bd2649342 100644 --- a/route/bgp_path_manager.go +++ b/route/bgp_path_manager.go @@ -7,7 +7,7 @@ import ( // BGPPathManager is a component used to deduplicate BGP Path objects type BGPPathManager struct { - paths map[BGPPath]*BGPPathCounter + paths map[string]*BGPPathCounter mu sync.Mutex } @@ -24,7 +24,7 @@ func NewBGPPathManager() *BGPPathManager { } func (m *BGPPathManager) lookup(p BGPPath) *BGPPath { - pathCounter, ok := m.paths[p] + pathCounter, ok := m.paths[p.ComputeHash()] if !ok { return nil } @@ -37,15 +37,17 @@ func (m *BGPPathManager) AddPath(p BGPPath) *BGPPath { m.mu.Lock() defer m.mu.Unlock() + hash := p.ComputeHash() + q := m.lookup(p) if q == nil { - m.paths[p] = &BGPPathCounter{ + m.paths[hash] = &BGPPathCounter{ path: &p, } } - m.paths[p].usageCount++ - return m.paths[p].path + m.paths[hash].usageCount++ + return m.paths[hash].path } // RemovePath notifies us that there is one user less for path p @@ -58,8 +60,10 @@ func (m *BGPPathManager) RemovePath(p BGPPath) { return } - m.paths[p].usageCount-- - if m.paths[p].usageCount == 0 { - delete(m.paths, p) + hash := p.ComputeHash() + + m.paths[hash].usageCount-- + if m.paths[hash].usageCount == 0 { + delete(m.paths, hash) } } diff --git a/route/bgp_test.go b/route/bgp_test.go new file mode 100644 index 0000000000000000000000000000000000000000..263f3471afe62819cf2d267751306163fa348bae --- /dev/null +++ b/route/bgp_test.go @@ -0,0 +1,28 @@ +package route + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestComputeHash(t *testing.T) { + p := &BGPPath{ + ASPath: "123 456", + BGPIdentifier: 1, + Communities: "(123, 456)", + EBGP: false, + LargeCommunities: "(1, 2, 3)", + LocalPref: 100, + MED: 1, + NextHop: 100, + PathIdentifier: 5, + Source: 4, + } + + assert.Equal(t, "24d5b7681ab221b464a2c772e828628482cbfa4d5c6aac7a8285d33ef99b868a", p.ComputeHash()) + + p.LocalPref = 150 + + assert.NotEqual(t, "24d5b7681ab221b464a2c772e828628482cbfa4d5c6aac7a8285d33ef99b868a", p.ComputeHash()) +} diff --git a/routingtable/adjRIBOut/path_id_manager.go b/routingtable/adjRIBOut/path_id_manager.go index 377ce9b3057101b6c1561d0124d9802f83c74faf..795d5f96b224681cbb05af56b69fa465b1ce5243 100644 --- a/routingtable/adjRIBOut/path_id_manager.go +++ b/routingtable/adjRIBOut/path_id_manager.go @@ -11,7 +11,7 @@ var maxUint32 = ^uint32(0) // pathIDManager manages BGP path identifiers for add-path. This is no thread safe (and doesn't need to be). type pathIDManager struct { ids map[uint32]uint64 - idByPath map[route.BGPPath]uint32 + idByPath map[string]uint32 last uint32 used uint32 } @@ -19,13 +19,15 @@ type pathIDManager struct { func newPathIDManager() *pathIDManager { return &pathIDManager{ ids: make(map[uint32]uint64), - idByPath: make(map[route.BGPPath]uint32), + idByPath: make(map[string]uint32), } } func (fm *pathIDManager) addPath(p *route.Path) (uint32, error) { - if _, exists := fm.idByPath[*p.BGPPath]; exists { - id := fm.idByPath[*p.BGPPath] + hash := p.BGPPath.ComputeHash() + + if _, exists := fm.idByPath[hash]; exists { + id := fm.idByPath[hash] fm.ids[id]++ return id, nil } @@ -43,7 +45,7 @@ func (fm *pathIDManager) addPath(p *route.Path) (uint32, error) { break } - fm.idByPath[*p.BGPPath] = fm.last + fm.idByPath[hash] = fm.last fm.ids[fm.last] = 1 fm.used++ @@ -51,15 +53,17 @@ func (fm *pathIDManager) addPath(p *route.Path) (uint32, error) { } func (fm *pathIDManager) releasePath(p *route.Path) (uint32, error) { - if _, exists := fm.idByPath[*p.BGPPath]; !exists { + hash := p.BGPPath.ComputeHash() + + if _, exists := fm.idByPath[hash]; !exists { return 0, fmt.Errorf("ID not found for path: %s", p.Print()) } - id := fm.idByPath[*p.BGPPath] + id := fm.idByPath[hash] fm.ids[id]-- if fm.ids[id] == 0 { - delete(fm.ids, fm.idByPath[*p.BGPPath]) - delete(fm.idByPath, *p.BGPPath) + delete(fm.ids, fm.idByPath[hash]) + delete(fm.idByPath, hash) } return id, nil