Skip to content

Commit 815ca4c

Browse files
Sergey Klyausfujita
Sergey Klyaus
authored andcommitted
Do not preserve MPReachNLRI attribute the way we receive it from wire
Right now table.ProcessMessage() rebuilds attribute list, but preserves MPReachNLRI attribute as is. This causes trouble if GoBGP has a neighbor that merges multiple NLRIs having same nexthop in a single BGP update message and if GoBGP has a watcher. Thus watcher receives up to n^2 nlris (n paths each containing n NLRIs) causing enormous memory consumption (since serialization/deserialization removes deduplication). Since having all NLRIs in each path is useless in most cases, the behavior is changed so only relevant NLRI is present in BGP path. In our tests memory consumption of such watcher had reduced from 19 Gb to 2Gb (with around 300K routes in RIB).
1 parent 08a001e commit 815ca4c

File tree

2 files changed

+57
-13
lines changed

2 files changed

+57
-13
lines changed

internal/pkg/table/table_manager.go

+17-4
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,19 @@ func ProcessMessage(m *bgp.BGPMessage, peerInfo *PeerInfo, timestamp time.Time)
8888
pathList = append(pathList, p)
8989
}
9090
if reach != nil {
91-
reachAttrs := make([]bgp.PathAttributeInterface, len(attrs)+1)
92-
copy(reachAttrs, attrs)
93-
// we sort attributes when creating a bgp message from paths
94-
reachAttrs[len(reachAttrs)-1] = reach
91+
nexthop := reach.Nexthop.String()
9592

9693
for _, nlri := range reach.Value {
9794
// when build path from reach
9895
// reachAttrs might not contain next_hop if `attrs` does not have one
9996
// this happens when a MP peer send update to gobgp
10097
// However nlri is always populated because how we build the path
10198
// path.info{nlri: nlri}
99+
// Compute a new attribute array for each path with one NLRI to make serialization
100+
// of path attrs faster
101+
nlriAttr := bgp.NewPathAttributeMpReachNLRI(nexthop, []bgp.AddrPrefixInterface{nlri})
102+
reachAttrs := makeAttributeList(attrs, nlriAttr)
103+
102104
p := NewPath(peerInfo, nlri, false, reachAttrs, timestamp, false)
103105
p.SetHash(hash)
104106
pathList = append(pathList, p)
@@ -111,6 +113,17 @@ func ProcessMessage(m *bgp.BGPMessage, peerInfo *PeerInfo, timestamp time.Time)
111113
return pathList
112114
}
113115

116+
func makeAttributeList(
117+
attrs []bgp.PathAttributeInterface, reach *bgp.PathAttributeMpReachNLRI,
118+
) []bgp.PathAttributeInterface {
119+
reachAttrs := make([]bgp.PathAttributeInterface, len(attrs)+1)
120+
copy(reachAttrs, attrs)
121+
// we sort attributes when creating a bgp message from paths
122+
reachAttrs[len(reachAttrs)-1] = reach
123+
124+
return reachAttrs
125+
}
126+
114127
type TableManager struct {
115128
Tables map[bgp.RouteFamily]*Table
116129
Vrfs map[string]*Vrf

internal/pkg/table/table_manager_test.go

+40-9
Original file line numberDiff line numberDiff line change
@@ -1979,35 +1979,37 @@ func TestProcessBGPUpdate_multiple_nlri_ipv6(t *testing.T) {
19791979

19801980
// check PathAttribute
19811981
checkPattr := func(expected *bgp.BGPMessage, actual *Path) {
1982-
pathAttributes := expected.Body.(*bgp.BGPUpdate).PathAttributes
1983-
pathNexthop := pathAttributes[4]
1982+
bgpPathAttributes := expected.Body.(*bgp.BGPUpdate).PathAttributes
1983+
bgpUpdateNexthop := bgpPathAttributes[4]
1984+
expectedNexthopAttr := bgp.NewPathAttributeMpReachNLRI(
1985+
bgpUpdateNexthop.(*bgp.PathAttributeMpReachNLRI).Nexthop.String(),
1986+
[]bgp.AddrPrefixInterface{actual.GetNlri()})
19841987
attr := actual.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
1985-
expectedNexthopAttr := attr.(*bgp.PathAttributeMpReachNLRI)
1988+
pathNexthop := attr.(*bgp.PathAttributeMpReachNLRI)
19861989
assert.Equal(t, expectedNexthopAttr, pathNexthop)
19871990

1988-
expectedOrigin := pathAttributes[0]
1991+
expectedOrigin := bgpPathAttributes[0]
19891992
attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
19901993
pathOrigin := attr.(*bgp.PathAttributeOrigin)
19911994
assert.Equal(t, expectedOrigin, pathOrigin)
19921995

1993-
expectedAsPath := pathAttributes[1]
1996+
expectedAsPath := bgpPathAttributes[1]
19941997
attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
19951998
pathAspath := attr.(*bgp.PathAttributeAsPath)
19961999
assert.Equal(t, expectedAsPath, pathAspath)
19972000

1998-
expectedMed := pathAttributes[2]
2001+
expectedMed := bgpPathAttributes[2]
19992002
attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
20002003
pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
20012004
assert.Equal(t, expectedMed, pathMed)
20022005

2003-
expectedLocalpref := pathAttributes[3]
2006+
expectedLocalpref := bgpPathAttributes[3]
20042007
attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_LOCAL_PREF)
20052008
localpref := attr.(*bgp.PathAttributeLocalPref)
20062009
assert.Equal(t, expectedLocalpref, localpref)
20072010

20082011
// check PathAttribute length
2009-
assert.Equal(t, len(pathAttributes), len(actual.GetPathAttrs()))
2010-
2012+
assert.Equal(t, len(bgpPathAttributes), len(actual.GetPathAttrs()))
20112013
}
20122014

20132015
checkBestPathResult := func(rf bgp.RouteFamily, prefix, nexthop string, p *Path, m *bgp.BGPMessage) {
@@ -2105,6 +2107,35 @@ func TestProcessBGPUpdate_multiple_nlri_ipv6(t *testing.T) {
21052107

21062108
}
21072109

2110+
func TestProcessBGPUpdate_multiple_nlri_ipv4_split(t *testing.T) {
2111+
tm := NewTableManager(logger, []bgp.RouteFamily{bgp.RF_IPv4_UC})
2112+
2113+
origin := bgp.NewPathAttributeOrigin(0)
2114+
aspath := createAsPathAttribute([]uint32{65000, 65100, 65200})
2115+
mpReach := createMpReach("10.50.60.70", []bgp.AddrPrefixInterface{
2116+
bgp.NewIPAddrPrefix(32, "10.0.0.1"),
2117+
bgp.NewIPAddrPrefix(32, "10.0.0.2"),
2118+
bgp.NewIPAddrPrefix(32, "10.0.0.3"),
2119+
})
2120+
med := bgp.NewPathAttributeMultiExitDisc(200)
2121+
localpref := bgp.NewPathAttributeLocalPref(200)
2122+
pathAttributes := []bgp.PathAttributeInterface{
2123+
mpReach, origin, aspath, med, localpref,
2124+
}
2125+
bgpMessage := bgp.NewBGPUpdateMessage(nil, pathAttributes, nil)
2126+
2127+
peer1 := peerR1()
2128+
pList, err := tm.ProcessUpdate(peer1, bgpMessage)
2129+
assert.Equal(t, len(mpReach.Value), len(pList))
2130+
for i, p := range pList {
2131+
attr := p.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI).(*bgp.PathAttributeMpReachNLRI)
2132+
assert.Equal(t, mpReach.Nexthop, attr.Nexthop)
2133+
assert.Equal(t, []bgp.AddrPrefixInterface{mpReach.Value[i]}, attr.Value)
2134+
}
2135+
assert.NoError(t, err)
2136+
2137+
}
2138+
21082139
func TestProcessBGPUpdate_Timestamp(t *testing.T) {
21092140
origin := bgp.NewPathAttributeOrigin(0)
21102141
aspathParam := []bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, []uint32{65000})}

0 commit comments

Comments
 (0)