Skip to content

Commit 4d9cd2b

Browse files
committed
fix: fix route stuck on eBGP filterpath
1 parent ae72f0f commit 4d9cd2b

File tree

2 files changed

+182
-39
lines changed

2 files changed

+182
-39
lines changed

pkg/server/server.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -551,9 +551,24 @@ func filterpath(peer *peer, path, old *table.Path) *table.Path {
551551
// Do not filter local (static) routes with as-path loop
552552
// if configured to bypass these checks in the peer
553553
// as-path options config.
554-
if path.IsLocal() && !peer.allowAsPathLoopLocal() {
555-
return nil
556-
} else if !path.IsLocal() {
554+
if !path.IsLocal() || !peer.allowAsPathLoopLocal() {
555+
if !path.IsWithdraw && old != nil {
556+
// A new best path was selected, but we cannot advertise it to this peer
557+
// due to as-loop. In this case, we MUST explicitly withdraw the
558+
// old path we previously advertised to prevent a stucked route
559+
// on the peer, which would lead to a traffic blackhole.
560+
//
561+
// Example: routers from AS A: A1 and A2, AS B: B1
562+
// A1 <eBGP> B1, A2 <eBGP> B1, no iBGP
563+
// All of them announce prefix P
564+
// Paths on A1, A2: [local, B1]
565+
// Paths on B1: [local, A1, A2]
566+
// When B1 receive local prefix withdraw it choose new best
567+
// for example from A1. Then it send. explicit withdraw for A1
568+
// and try to implicit withdraw for other peers. But we have AS-Loop for
569+
// A2 and path stuck. So in this case B1 should send explicit withdraw for old path.
570+
return old.Clone(true)
571+
}
557572
return nil
558573
}
559574
}

pkg/server/server_test.go

Lines changed: 164 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,48 +1594,65 @@ func runNewServer(t *testing.T, as uint32, routerID string, listenPort int32) *B
15941594
return s
15951595
}
15961596

1597-
func peerServers(t *testing.T, ctx context.Context, servers []*BgpServer, families []oc.AfiSafiType) error {
1597+
type peerOption func(peer *BgpServer, g *oc.Global, p *oc.Neighbor)
1598+
1599+
func setPeerAddressOpt(peer *BgpServer, g *oc.Global, p *oc.Neighbor) {
1600+
p.Transport.Config.LocalAddress = g.Config.LocalAddressList[0]
1601+
p.Config.NeighborAddress = peer.bgpConfig.Global.Config.LocalAddressList[0]
1602+
}
1603+
1604+
func peerTwoServers(t *testing.T, ctx context.Context, server, peer *BgpServer, families []oc.AfiSafiType, isPassive bool, opts ...peerOption) error {
1605+
neighborConfig := &oc.Neighbor{
1606+
Config: oc.NeighborConfig{
1607+
NeighborAddress: netip.MustParseAddr("127.0.0.1"),
1608+
PeerAs: peer.bgpConfig.Global.Config.As,
1609+
},
1610+
AfiSafis: oc.AfiSafis{},
1611+
Transport: oc.Transport{
1612+
Config: oc.TransportConfig{
1613+
RemotePort: uint16(peer.bgpConfig.Global.Config.Port),
1614+
},
1615+
},
1616+
Timers: oc.Timers{
1617+
Config: oc.TimersConfig{
1618+
ConnectRetry: 1,
1619+
IdleHoldTimeAfterReset: 1,
1620+
},
1621+
},
1622+
}
1623+
1624+
for _, opt := range opts {
1625+
opt(peer, &server.bgpConfig.Global, neighborConfig)
1626+
}
1627+
1628+
if isPassive {
1629+
neighborConfig.Transport.Config.PassiveMode = true
1630+
}
1631+
1632+
for _, family := range families {
1633+
neighborConfig.AfiSafis = append(neighborConfig.AfiSafis, oc.AfiSafi{
1634+
Config: oc.AfiSafiConfig{
1635+
AfiSafiName: family,
1636+
Enabled: true,
1637+
},
1638+
})
1639+
}
1640+
1641+
if err := server.AddPeer(ctx, &api.AddPeerRequest{Peer: oc.NewPeerFromConfigStruct(neighborConfig)}); err != nil {
1642+
t.Fatal(err)
1643+
}
1644+
return nil
1645+
}
1646+
1647+
func peerServers(t *testing.T, ctx context.Context, servers []*BgpServer, families []oc.AfiSafiType, opts ...peerOption) error {
15981648
for i, server := range servers {
15991649
for j, peer := range servers {
16001650
if i == j {
16011651
continue
16021652
}
1603-
1604-
neighborConfig := &oc.Neighbor{
1605-
Config: oc.NeighborConfig{
1606-
NeighborAddress: netip.MustParseAddr("127.0.0.1"),
1607-
PeerAs: peer.bgpConfig.Global.Config.As,
1608-
},
1609-
AfiSafis: oc.AfiSafis{},
1610-
Transport: oc.Transport{
1611-
Config: oc.TransportConfig{
1612-
RemotePort: uint16(peer.bgpConfig.Global.Config.Port),
1613-
},
1614-
},
1615-
Timers: oc.Timers{
1616-
Config: oc.TimersConfig{
1617-
ConnectRetry: 1,
1618-
IdleHoldTimeAfterReset: 1,
1619-
},
1620-
},
1621-
}
1622-
16231653
// first server to get neighbor config is passive to hopefully make handshake faster
1624-
if j > i {
1625-
neighborConfig.Transport.Config.PassiveMode = true
1626-
}
1627-
1628-
for _, family := range families {
1629-
neighborConfig.AfiSafis = append(neighborConfig.AfiSafis, oc.AfiSafi{
1630-
Config: oc.AfiSafiConfig{
1631-
AfiSafiName: family,
1632-
Enabled: true,
1633-
},
1634-
})
1635-
}
1636-
1637-
if err := server.AddPeer(ctx, &api.AddPeerRequest{Peer: oc.NewPeerFromConfigStruct(neighborConfig)}); err != nil {
1638-
t.Fatal(err)
1654+
if err := peerTwoServers(t, ctx, server, peer, families, i < j, opts...); err != nil {
1655+
return err
16391656
}
16401657
}
16411658
}
@@ -3003,3 +3020,114 @@ func TestAddDefinedSetReplace(t *testing.T) {
30033020
assert.Equal("replaceme", ns[0].Name)
30043021
assert.Equal([]string{"203.0.113.2/32"}, ns[0].List)
30053022
}
3023+
3024+
func TestEBGPRouteStuck(test *testing.T) {
3025+
var peers []*BgpServer
3026+
for i, s := range []struct {
3027+
routerId string
3028+
asn uint32
3029+
}{
3030+
{routerId: "1.1.1.1", asn: 1},
3031+
{routerId: "2.2.2.1", asn: 2},
3032+
{routerId: "2.2.2.2", asn: 2},
3033+
} {
3034+
peer := NewBgpServer()
3035+
peer.logger.SetLevel(log.InfoLevel)
3036+
go peer.Serve()
3037+
3038+
peers = append(peers, peer)
3039+
3040+
err := peer.StartBgp(context.Background(), &api.StartBgpRequest{
3041+
Global: &api.Global{
3042+
Asn: s.asn,
3043+
RouterId: s.routerId,
3044+
ListenAddresses: []string{fmt.Sprintf("127.0.0.%d", 100+i)},
3045+
ListenPort: 10179,
3046+
},
3047+
})
3048+
require.NoError(test, err)
3049+
}
3050+
3051+
wg := waitEstablished(peers[0])
3052+
wg1 := waitEstablished(peers[1])
3053+
wg2 := waitEstablished(peers[2])
3054+
// Use only eBGP
3055+
for i, server := range peers {
3056+
for j, peer := range peers {
3057+
if i == j || server.bgpConfig.Global.Config.As == peer.bgpConfig.Global.Config.As {
3058+
continue
3059+
}
3060+
ctx := context.Background()
3061+
if err := peerTwoServers(test, ctx, server, peer, []oc.AfiSafiType{oc.AFI_SAFI_TYPE_IPV4_UNICAST}, i < j, setPeerAddressOpt); err != nil {
3062+
assert.NoError(test, err)
3063+
}
3064+
}
3065+
}
3066+
3067+
wg.Wait()
3068+
wg1.Wait()
3069+
wg2.Wait()
3070+
3071+
family4 := &api.Family{
3072+
Afi: api.Family_AFI_IP,
3073+
Safi: api.Family_SAFI_UNICAST,
3074+
}
3075+
attrs := []*api.Attribute{
3076+
{
3077+
Attr: &api.Attribute_Origin{Origin: &api.OriginAttribute{
3078+
Origin: 0,
3079+
}},
3080+
},
3081+
{
3082+
Attr: &api.Attribute_NextHop{NextHop: &api.NextHopAttribute{
3083+
NextHop: "10.0.0.1",
3084+
}},
3085+
},
3086+
}
3087+
3088+
nlri := &api.NLRI{Nlri: &api.NLRI_Prefix{Prefix: &api.IPAddressPrefix{
3089+
Prefix: "10.1.0.0",
3090+
PrefixLen: 24,
3091+
}}}
3092+
3093+
assertPathCount := func(t assert.TestingT, peer *BgpServer, expected int) {
3094+
var info *table.TableInfo
3095+
if peer.active() == nil {
3096+
info, _ = peer.getRibInfo("", bgp.RF_IPv4_UC)
3097+
} else {
3098+
info = peer.globalRib.Tables[bgp.RF_IPv4_UC].Info()
3099+
}
3100+
if assert.NotNil(t, info) {
3101+
assert.Equal(t, expected, info.NumPath)
3102+
}
3103+
}
3104+
3105+
path := &api.Path{
3106+
Family: family4,
3107+
Nlri: nlri,
3108+
Pattrs: attrs,
3109+
}
3110+
addPaths := func(peers []*BgpServer, path *api.Path) {
3111+
for _, peer := range peers {
3112+
_, err := peer.AddPath(apiutil.AddPathRequest{Paths: []*apiutil.Path{mustApi2apiutilPath(path)}})
3113+
assert.NoError(test, err)
3114+
}
3115+
}
3116+
3117+
addPaths(peers, path)
3118+
3119+
assert.EventuallyWithT(test, func(collect *assert.CollectT) {
3120+
assertPathCount(collect, peers[0], 3)
3121+
assertPathCount(collect, peers[1], 2)
3122+
assertPathCount(collect, peers[2], 2)
3123+
}, 5*time.Second, 1*time.Millisecond)
3124+
3125+
err := peers[0].DeletePath(apiutil.DeletePathRequest{Paths: []*apiutil.Path{mustApi2apiutilPath(path)}})
3126+
assert.NoError(test, err)
3127+
3128+
assert.EventuallyWithT(test, func(collect *assert.CollectT) {
3129+
assertPathCount(collect, peers[0], 2)
3130+
assertPathCount(collect, peers[1], 1)
3131+
assertPathCount(collect, peers[2], 1)
3132+
}, 20*time.Second, 1*time.Millisecond)
3133+
}

0 commit comments

Comments
 (0)