Skip to content

Commit 9794f49

Browse files
committed
Move SelectionOptions and UseMultiplePath to server-local vars
In destination.go there were two global variables used for controlling path and multi-path support for BGP servers, which were populated and modified by gRPC/public API calls. In scenarios where multiple BGP servers are running using the public API it would mean that the last server created would override the options for all running servers. It also lead to data races (Confirmed by using Go's data race detector) that could crash Go's runtime as the value would be overridden unexpectedly. To resolve this, both options have been moved into the TableManager itself which takes a pointer option to both structs populated via the StartBgp API call. These are then passed to the internal methods as new function parameters, ensuring that every server has a unique copy of these structs. All tests were updated to conform to the new API.
1 parent 146b2b8 commit 9794f49

File tree

7 files changed

+107
-105
lines changed

7 files changed

+107
-105
lines changed

internal/pkg/table/destination.go

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@ import (
2828
"github.com/osrg/gobgp/v3/pkg/packet/bgp"
2929
)
3030

31-
var SelectionOptions oc.RouteSelectionOptionsConfig
32-
var UseMultiplePaths oc.UseMultiplePathsConfig
33-
3431
type BestPathReason uint8
3532

3633
const (
@@ -228,7 +225,7 @@ func (dd *Destination) GetMultiBestPath(id string) []*Path {
228225
//
229226
// Modifies destination's state related to stored paths. Removes withdrawn
230227
// paths from known paths. Also, adds new paths to known paths.
231-
func (dest *Destination) Calculate(logger log.Logger, newPath *Path) *Update {
228+
func (dest *Destination) Calculate(logger log.Logger, newPath *Path, selectionOptions *oc.RouteSelectionOptionsConfig) *Update {
232229
oldKnownPathList := make([]*Path, len(dest.knownPathList))
233230
copy(oldKnownPathList, dest.knownPathList)
234231

@@ -255,7 +252,7 @@ func (dest *Destination) Calculate(logger log.Logger, newPath *Path) *Update {
255252
}
256253
}
257254
// Compute new best path
258-
dest.computeKnownBestPath()
255+
dest.computeKnownBestPath(selectionOptions)
259256

260257
l := make([]*Path, len(dest.knownPathList))
261258
copy(l, dest.knownPathList)
@@ -344,8 +341,8 @@ func (dest *Destination) implicitWithdraw(logger log.Logger, newPath *Path) {
344341
}
345342
}
346343

347-
func (dest *Destination) computeKnownBestPath() (*Path, BestPathReason, error) {
348-
if SelectionOptions.DisableBestPathSelection {
344+
func (dest *Destination) computeKnownBestPath(selectionOptions *oc.RouteSelectionOptionsConfig) (*Path, BestPathReason, error) {
345+
if selectionOptions.DisableBestPathSelection {
349346
return nil, BPR_DISABLED, nil
350347
}
351348

@@ -366,7 +363,7 @@ func (dest *Destination) computeKnownBestPath() (*Path, BestPathReason, error) {
366363
}
367364
return dest.knownPathList[0], BPR_ONLY_PATH, nil
368365
}
369-
reason := dest.sort()
366+
reason := dest.sort(selectionOptions)
370367
newBest := dest.knownPathList[0]
371368
// If the first path has the invalidated next-hop, which evaluated by IGP,
372369
// returns no path with the reason of the next-hop reachability.
@@ -376,7 +373,7 @@ func (dest *Destination) computeKnownBestPath() (*Path, BestPathReason, error) {
376373
return newBest, reason, nil
377374
}
378375

379-
func (dst *Destination) sort() BestPathReason {
376+
func (dst *Destination) sort(selectionOptions *oc.RouteSelectionOptionsConfig) BestPathReason {
380377
reason := BPR_UNKNOWN
381378

382379
sort.SliceStable(dst.knownPathList, func(i, j int) bool {
@@ -438,15 +435,15 @@ func (dst *Destination) sort() BestPathReason {
438435
reason = BPR_LOCAL_ORIGIN
439436
}
440437
if better == nil {
441-
better = compareByASPath(path1, path2)
438+
better = compareByASPath(path1, path2, selectionOptions)
442439
reason = BPR_ASPATH
443440
}
444441
if better == nil {
445442
better = compareByOrigin(path1, path2)
446443
reason = BPR_ORIGIN
447444
}
448445
if better == nil {
449-
better = compareByMED(path1, path2)
446+
better = compareByMED(path1, path2, selectionOptions)
450447
reason = BPR_MED
451448
}
452449
if better == nil {
@@ -457,11 +454,11 @@ func (dst *Destination) sort() BestPathReason {
457454
// compareByIGPCost was a no-op and was removed.
458455

459456
if better == nil {
460-
better = compareByAge(path1, path2)
457+
better = compareByAge(path1, path2, selectionOptions)
461458
reason = BPR_OLDER
462459
}
463460
if better == nil {
464-
better, _ = compareByRouterID(path1, path2)
461+
better, _ = compareByRouterID(path1, path2, selectionOptions)
465462
reason = BPR_ROUTER_ID
466463
}
467464
if better == nil {
@@ -521,7 +518,7 @@ func (u *Update) GetWithdrawnPath() []*Path {
521518
return l
522519
}
523520

524-
func (u *Update) GetChanges(id string, as uint32, peerDown bool) (*Path, *Path, []*Path) {
521+
func (u *Update) GetChanges(id string, as uint32, peerDown bool, useMultiplePaths *oc.UseMultiplePathsConfig) (*Path, *Path, []*Path) {
525522
best, old := func(id string) (*Path, *Path) {
526523
old := getBestPath(id, as, u.OldKnownPathList)
527524
best := getBestPath(id, as, u.KnownPathList)
@@ -564,7 +561,7 @@ func (u *Update) GetChanges(id string, as uint32, peerDown bool) (*Path, *Path,
564561

565562
var multi []*Path
566563

567-
if id == GLOBAL_RIB_NAME && UseMultiplePaths.Enabled {
564+
if id == GLOBAL_RIB_NAME && useMultiplePaths.Enabled {
568565
diff := func(lhs, rhs []*Path) bool {
569566
if len(lhs) != len(rhs) {
570567
return true
@@ -659,12 +656,12 @@ func compareByLocalOrigin(path1, path2 *Path) *Path {
659656
return nil
660657
}
661658

662-
func compareByASPath(path1, path2 *Path) *Path {
659+
func compareByASPath(path1, path2 *Path, selectionOptions *oc.RouteSelectionOptionsConfig) *Path {
663660
// Calculated the best-paths by comparing as-path lengths.
664661
//
665662
// Shortest as-path length is preferred. If both path have same lengths,
666663
// we return None.
667-
if SelectionOptions.IgnoreAsPathLength {
664+
if selectionOptions.IgnoreAsPathLength {
668665
return nil
669666
}
670667

@@ -706,7 +703,7 @@ func compareByOrigin(path1, path2 *Path) *Path {
706703
}
707704
}
708705

709-
func compareByMED(path1, path2 *Path) *Path {
706+
func compareByMED(path1, path2 *Path, selectionOptions *oc.RouteSelectionOptionsConfig) *Path {
710707
// Select the path based with lowest MED value.
711708
//
712709
// If both paths have same MED, return None.
@@ -739,7 +736,7 @@ func compareByMED(path1, path2 *Path) *Path {
739736
return firstAS(path1) != 0 && firstAS(path1) == firstAS(path2)
740737
}()
741738

742-
if SelectionOptions.AlwaysCompareMed || isInternal || isSameAS {
739+
if selectionOptions.AlwaysCompareMed || isInternal || isSameAS {
743740
getMed := func(path *Path) uint32 {
744741
attribute := path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
745742
if attribute == nil {
@@ -784,7 +781,7 @@ func compareByASNumber(path1, path2 *Path) *Path {
784781
return nil
785782
}
786783

787-
func compareByRouterID(path1, path2 *Path) (*Path, error) {
784+
func compareByRouterID(path1, path2 *Path, selectionOptions *oc.RouteSelectionOptionsConfig) (*Path, error) {
788785
// Select the route received from the peer with the lowest BGP router ID.
789786
//
790787
// If both paths are eBGP paths, then we do not do any tie breaking, i.e we do
@@ -799,11 +796,11 @@ func compareByRouterID(path1, path2 *Path) (*Path, error) {
799796

800797
// If both paths are from eBGP peers, then according to RFC we need
801798
// not tie break using router id.
802-
if !SelectionOptions.ExternalCompareRouterId && !path1.IsIBGP() && !path2.IsIBGP() {
799+
if !selectionOptions.ExternalCompareRouterId && !path1.IsIBGP() && !path2.IsIBGP() {
803800
return nil, nil
804801
}
805802

806-
if !SelectionOptions.ExternalCompareRouterId && path1.IsIBGP() != path2.IsIBGP() {
803+
if !selectionOptions.ExternalCompareRouterId && path1.IsIBGP() != path2.IsIBGP() {
807804
return nil, fmt.Errorf("this method does not support comparing ebgp with ibgp path")
808805
}
809806

@@ -844,8 +841,8 @@ func compareByNeighborAddress(path1, path2 *Path) *Path {
844841
return nil
845842
}
846843

847-
func compareByAge(path1, path2 *Path) *Path {
848-
if !path1.IsIBGP() && !path2.IsIBGP() && !SelectionOptions.ExternalCompareRouterId {
844+
func compareByAge(path1, path2 *Path, selectionOptions *oc.RouteSelectionOptionsConfig) *Path {
845+
if !path1.IsIBGP() && !path2.IsIBGP() && !selectionOptions.ExternalCompareRouterId {
849846
age1 := path1.GetTimestamp().UnixNano()
850847
age2 := path2.GetTimestamp().UnixNano()
851848
if age1 == age2 {

internal/pkg/table/destination_test.go

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"testing"
2323
"time"
2424

25+
"github.com/osrg/gobgp/v3/internal/pkg/config"
2526
"github.com/osrg/gobgp/v3/pkg/packet/bgp"
2627

2728
"github.com/stretchr/testify/assert"
@@ -83,7 +84,7 @@ func TestCalculate2(t *testing.T) {
8384
path1 := ProcessMessage(update1, peer1, time.Now())[0]
8485

8586
d := NewDestination(nlri, 0)
86-
d.Calculate(logger, path1)
87+
d.Calculate(logger, path1, &config.RouteSelectionOptionsConfig {})
8788

8889
// suppose peer2 sends grammaatically correct but semantically flawed update message
8990
// which has a withdrawal nlri not advertised before
@@ -92,7 +93,7 @@ func TestCalculate2(t *testing.T) {
9293
path2 := ProcessMessage(update2, peer2, time.Now())[0]
9394
assert.Equal(t, path2.IsWithdraw, true)
9495

95-
d.Calculate(logger, path2)
96+
d.Calculate(logger, path2, &config.RouteSelectionOptionsConfig {})
9697

9798
// we have a path from peer1 here
9899
assert.Equal(t, len(d.knownPathList), 1)
@@ -102,7 +103,7 @@ func TestCalculate2(t *testing.T) {
102103
path3 := ProcessMessage(update3, peer2, time.Now())[0]
103104
assert.Equal(t, path3.IsWithdraw, false)
104105

105-
d.Calculate(logger, path3)
106+
d.Calculate(logger, path3, &config.RouteSelectionOptionsConfig {})
106107

107108
// this time, we have paths from peer1 and peer2
108109
assert.Equal(t, len(d.knownPathList), 2)
@@ -112,7 +113,7 @@ func TestCalculate2(t *testing.T) {
112113
update4 := bgp.NewBGPUpdateMessage(nil, pathAttributes, []*bgp.IPAddrPrefix{nlri})
113114
path4 := ProcessMessage(update4, peer3, time.Now())[0]
114115

115-
d.Calculate(logger, path4)
116+
d.Calculate(logger, path4, &config.RouteSelectionOptionsConfig {})
116117

117118
// we must have paths from peer1, peer2 and peer3
118119
assert.Equal(t, len(d.knownPathList), 3)
@@ -156,7 +157,7 @@ func TestMedTieBreaker(t *testing.T) {
156157
}()
157158

158159
// same AS
159-
assert.Equal(t, compareByMED(p0, p1), p0)
160+
assert.Equal(t, compareByMED(p0, p1, &config.RouteSelectionOptionsConfig {}), p0)
160161

161162
p2 := func() *Path {
162163
aspath := bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{65003})})
@@ -165,7 +166,7 @@ func TestMedTieBreaker(t *testing.T) {
165166
}()
166167

167168
// different AS
168-
assert.Equal(t, compareByMED(p0, p2), (*Path)(nil))
169+
assert.Equal(t, compareByMED(p0, p2, &config.RouteSelectionOptionsConfig {}), (*Path)(nil))
169170

170171
p3 := func() *Path {
171172
aspath := bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, []uint32{65003, 65004}), bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{65001, 65003})})
@@ -180,7 +181,7 @@ func TestMedTieBreaker(t *testing.T) {
180181
}()
181182

182183
// ignore confed
183-
assert.Equal(t, compareByMED(p3, p4), p3)
184+
assert.Equal(t, compareByMED(p3, p4, &config.RouteSelectionOptionsConfig {}), p3)
184185

185186
p5 := func() *Path {
186187
attrs := []bgp.PathAttributeInterface{bgp.NewPathAttributeMultiExitDisc(0)}
@@ -193,7 +194,7 @@ func TestMedTieBreaker(t *testing.T) {
193194
}()
194195

195196
// no aspath
196-
assert.Equal(t, compareByMED(p5, p6), p5)
197+
assert.Equal(t, compareByMED(p5, p6, &config.RouteSelectionOptionsConfig {}), p5)
197198
}
198199

199200
func TestTimeTieBreaker(t *testing.T) {
@@ -212,17 +213,19 @@ func TestTimeTieBreaker(t *testing.T) {
212213
path2 := ProcessMessage(updateMsg, peer2, time.Now().Add(-1*time.Hour))[0] // older than path1
213214

214215
d := NewDestination(nlri, 0)
215-
d.Calculate(logger, path1)
216-
d.Calculate(logger, path2)
216+
d.Calculate(logger, path1, &config.RouteSelectionOptionsConfig {})
217+
d.Calculate(logger, path2, &config.RouteSelectionOptionsConfig {})
217218

218219
assert.Equal(t, len(d.knownPathList), 2)
219220
assert.Equal(t, true, d.GetBestPath("", 0).GetSource().ID.Equal(net.IP{2, 2, 2, 2})) // path from peer2 win
220221

221222
// this option disables tie breaking by age
222-
SelectionOptions.ExternalCompareRouterId = true
223+
selectionOptions := config.RouteSelectionOptionsConfig {
224+
ExternalCompareRouterId: true,
225+
}
223226
d = NewDestination(nlri, 0)
224-
d.Calculate(logger, path1)
225-
d.Calculate(logger, path2)
227+
d.Calculate(logger, path1, &selectionOptions)
228+
d.Calculate(logger, path2, &selectionOptions)
226229

227230
assert.Equal(t, len(d.knownPathList), 2)
228231
assert.Equal(t, true, d.GetBestPath("", 0).GetSource().ID.Equal(net.IP{1, 1, 1, 1})) // path from peer1 win
@@ -315,7 +318,9 @@ func updateMsgD3() *bgp.BGPMessage {
315318
}
316319

317320
func TestMultipath(t *testing.T) {
318-
UseMultiplePaths.Enabled = true
321+
useMultiplePaths := config.UseMultiplePathsConfig {
322+
Enabled: true,
323+
}
319324
origin := bgp.NewPathAttributeOrigin(0)
320325
aspathParam := []bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, []uint32{65000})}
321326
aspath := bgp.NewPathAttributeAsPath(aspathParam)
@@ -347,17 +352,17 @@ func TestMultipath(t *testing.T) {
347352
path2 := ProcessMessage(updateMsg, peer2, time.Now())[0]
348353

349354
d := NewDestination(nlri[0], 0)
350-
d.Calculate(logger, path2)
355+
d.Calculate(logger, path2, &config.RouteSelectionOptionsConfig{})
351356

352-
best, old, multi := d.Calculate(logger, path1).GetChanges(GLOBAL_RIB_NAME, 0, false)
357+
best, old, multi := d.Calculate(logger, path1, &config.RouteSelectionOptionsConfig{}).GetChanges(GLOBAL_RIB_NAME, 0, false, &useMultiplePaths)
353358
assert.NotNil(t, best)
354359
assert.Equal(t, old, path2)
355360
assert.Equal(t, len(multi), 2)
356361
assert.Equal(t, len(d.GetKnownPathList(GLOBAL_RIB_NAME, 0)), 2)
357362

358363
path3 := path2.Clone(true)
359-
dd := d.Calculate(logger, path3)
360-
best, old, multi = dd.GetChanges(GLOBAL_RIB_NAME, 0, false)
364+
dd := d.Calculate(logger, path3, &config.RouteSelectionOptionsConfig{})
365+
best, old, multi = dd.GetChanges(GLOBAL_RIB_NAME, 0, false, &useMultiplePaths)
361366
assert.Nil(t, best)
362367
assert.Equal(t, old, path1)
363368
assert.Equal(t, len(multi), 1)
@@ -374,8 +379,8 @@ func TestMultipath(t *testing.T) {
374379
}
375380
updateMsg = bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
376381
path4 := ProcessMessage(updateMsg, peer3, time.Now())[0]
377-
dd = d.Calculate(logger, path4)
378-
best, _, multi = dd.GetChanges(GLOBAL_RIB_NAME, 0, false)
382+
dd = d.Calculate(logger, path4, &config.RouteSelectionOptionsConfig{})
383+
best, _, multi = dd.GetChanges(GLOBAL_RIB_NAME, 0, false, &useMultiplePaths)
379384
assert.NotNil(t, best)
380385
assert.Equal(t, len(multi), 1)
381386
assert.Equal(t, len(d.GetKnownPathList(GLOBAL_RIB_NAME, 0)), 2)
@@ -389,12 +394,10 @@ func TestMultipath(t *testing.T) {
389394
}
390395
updateMsg = bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
391396
path5 := ProcessMessage(updateMsg, peer2, time.Now())[0]
392-
best, _, multi = d.Calculate(logger, path5).GetChanges(GLOBAL_RIB_NAME, 0, false)
397+
best, _, multi = d.Calculate(logger, path5, &config.RouteSelectionOptionsConfig{}).GetChanges(GLOBAL_RIB_NAME, 0, false, &useMultiplePaths)
393398
assert.NotNil(t, best)
394399
assert.Equal(t, len(multi), 2)
395400
assert.Equal(t, len(d.GetKnownPathList(GLOBAL_RIB_NAME, 0)), 3)
396-
397-
UseMultiplePaths.Enabled = false
398401
}
399402

400403
func TestIdMap(t *testing.T) {

internal/pkg/table/table_manager.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323

2424
farm "github.com/dgryski/go-farm"
2525

26+
"github.com/osrg/gobgp/v3/pkg/config/oc"
2627
"github.com/osrg/gobgp/v3/pkg/log"
2728
"github.com/osrg/gobgp/v3/pkg/packet/bgp"
2829
)
@@ -114,14 +115,18 @@ func ProcessMessage(m *bgp.BGPMessage, peerInfo *PeerInfo, timestamp time.Time)
114115
type TableManager struct {
115116
Tables map[bgp.RouteFamily]*Table
116117
Vrfs map[string]*Vrf
118+
SelectionOptions *oc.RouteSelectionOptionsConfig
119+
UseMultiplePaths *oc.UseMultiplePathsConfig
117120
rfList []bgp.RouteFamily
118121
logger log.Logger
119122
}
120123

121-
func NewTableManager(logger log.Logger, rfList []bgp.RouteFamily) *TableManager {
124+
func NewTableManager(logger log.Logger, rfList []bgp.RouteFamily, selectionOptions *oc.RouteSelectionOptionsConfig, useMultiplePaths *oc.UseMultiplePathsConfig) *TableManager {
122125
t := &TableManager{
123126
Tables: make(map[bgp.RouteFamily]*Table),
124127
Vrfs: make(map[string]*Vrf),
128+
SelectionOptions: selectionOptions,
129+
UseMultiplePaths: useMultiplePaths,
125130
rfList: rfList,
126131
logger: logger,
127132
}
@@ -192,7 +197,7 @@ func (manager *TableManager) update(newPath *Path) *Update {
192197
t := manager.Tables[newPath.GetRouteFamily()]
193198
t.validatePath(newPath)
194199
dst := t.getOrCreateDest(newPath.GetNlri(), 64)
195-
u := dst.Calculate(manager.logger, newPath)
200+
u := dst.Calculate(manager.logger, newPath, manager.SelectionOptions)
196201
if len(dst.knownPathList) == 0 {
197202
t.deleteDest(dst)
198203
}
@@ -292,7 +297,7 @@ func (manager *TableManager) getDestinationCount(rfList []bgp.RouteFamily) int {
292297
}
293298

294299
func (manager *TableManager) GetBestPathList(id string, as uint32, rfList []bgp.RouteFamily) []*Path {
295-
if SelectionOptions.DisableBestPathSelection {
300+
if manager.SelectionOptions.DisableBestPathSelection {
296301
// Note: If best path selection disabled, there is no best path.
297302
return nil
298303
}
@@ -304,7 +309,7 @@ func (manager *TableManager) GetBestPathList(id string, as uint32, rfList []bgp.
304309
}
305310

306311
func (manager *TableManager) GetBestMultiPathList(id string, rfList []bgp.RouteFamily) [][]*Path {
307-
if !UseMultiplePaths.Enabled || SelectionOptions.DisableBestPathSelection {
312+
if !manager.UseMultiplePaths.Enabled || manager.SelectionOptions.DisableBestPathSelection {
308313
// Note: If multi path not enabled or best path selection disabled,
309314
// there is no best multi path.
310315
return nil

0 commit comments

Comments
 (0)