diff options
author | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2018-05-10 09:00:50 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2018-05-10 23:13:21 +0900 |
commit | 691d80e5d160b82c8b4dd1d83c959ecdffad318f (patch) | |
tree | 52d9a238bde9dd525c63bf15b8ede84ab85d56fe | |
parent | 5fc0ac2731ebbe0e812d493e837007068989faa7 (diff) |
make Path objects in rib read-only
Now you can read Path objects in rib safely. Nobody modifies
them. GetRib() API doesn't need to clone the objects. With full
routes, this avoid allocating temporary huge memory.
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r-- | api/grpc_server.go | 47 | ||||
-rw-r--r-- | client/client.go | 4 | ||||
-rw-r--r-- | gobgp/lib/path.go | 2 | ||||
-rw-r--r-- | server/peer.go | 231 | ||||
-rw-r--r-- | server/rpki.go | 24 | ||||
-rw-r--r-- | server/server.go | 333 | ||||
-rw-r--r-- | server/server_test.go | 5 | ||||
-rw-r--r-- | server/zclient.go | 2 | ||||
-rw-r--r-- | table/destination.go | 14 | ||||
-rw-r--r-- | table/policy.go | 10 |
10 files changed, 341 insertions, 331 deletions
diff --git a/api/grpc_server.go b/api/grpc_server.go index cec1c272..4d366482 100644 --- a/api/grpc_server.go +++ b/api/grpc_server.go @@ -369,7 +369,7 @@ func NewValidationFromTableStruct(v *table.Validation) *RPKIValidation { } } -func ToPathApi(path *table.Path) *Path { +func ToPathApi(path *table.Path, v *table.Validation) *Path { nlri := path.GetNlri() n, _ := nlri.Serialize() family := uint32(bgp.AfiSafiToRouteFamily(nlri.AFI(), nlri.SAFI())) @@ -381,13 +381,18 @@ func ToPathApi(path *table.Path) *Path { } return ret }(path.GetPathAttrs()) + vv := config.RPKI_VALIDATION_RESULT_TYPE_NONE.ToInt() + if v != nil { + vv = v.Status.ToInt() + } + p := &Path{ Nlri: n, Pattrs: pattrs, Age: path.GetTimestamp().Unix(), IsWithdraw: path.IsWithdraw, - Validation: int32(path.ValidationStatus().ToInt()), - ValidationDetail: NewValidationFromTableStruct(path.Validation()), + Validation: int32(vv), + ValidationDetail: NewValidationFromTableStruct(v), Family: family, Stale: path.IsStale(), IsFromExternal: path.IsFromExternal(), @@ -404,6 +409,14 @@ func ToPathApi(path *table.Path) *Path { return p } +func getValidation(v []*table.Validation, i int) *table.Validation { + if v == nil { + return nil + } else { + return v[i] + } +} + func (s *Server) GetRib(ctx context.Context, arg *GetRibRequest) (*GetRibResponse, error) { if arg == nil || arg.Table == nil { return nil, fmt.Errorf("invalid request") @@ -429,16 +442,17 @@ func (s *Server) GetRib(ctx context.Context, arg *GetRibRequest) (*GetRibRespons var in bool var err error var tbl *table.Table + var v []*table.Validation family := bgp.RouteFamily(arg.Table.Family) switch arg.Table.Type { case Resource_LOCAL, Resource_GLOBAL: - tbl, err = s.bgpServer.GetRib(arg.Table.Name, family, f()) + tbl, v, err = s.bgpServer.GetRib(arg.Table.Name, family, f()) case Resource_ADJ_IN: in = true fallthrough case Resource_ADJ_OUT: - tbl, err = s.bgpServer.GetAdjRib(arg.Table.Name, family, in, f()) + tbl, v, err = s.bgpServer.GetAdjRib(arg.Table.Name, family, in, f()) case Resource_VRF: tbl, err = s.bgpServer.GetVrfRib(arg.Table.Name, family, []*table.LookupPrefix{}) default: @@ -451,13 +465,15 @@ func (s *Server) GetRib(ctx context.Context, arg *GetRibRequest) (*GetRibRespons tblDsts := tbl.GetDestinations() dsts := make([]*Destination, 0, len(tblDsts)) + idx := 0 for _, dst := range tblDsts { dsts = append(dsts, &Destination{ Prefix: dst.GetNlri().String(), Paths: func(paths []*table.Path) []*Path { l := make([]*Path, 0, len(paths)) for i, p := range paths { - pp := ToPathApi(p) + pp := ToPathApi(p, getValidation(v, idx)) + idx++ switch arg.Table.Type { case Resource_LOCAL, Resource_GLOBAL: if i == 0 && !table.SelectionOptions.DisableBestPathSelection { @@ -494,14 +510,15 @@ func (s *Server) GetPath(arg *GetPathRequest, stream GobgpApi_GetPathServer) err family := bgp.RouteFamily(arg.Family) var tbl *table.Table var err error + var v []*table.Validation switch arg.Type { case Resource_LOCAL, Resource_GLOBAL: - tbl, err = s.bgpServer.GetRib(arg.Name, family, f()) + tbl, v, err = s.bgpServer.GetRib(arg.Name, family, f()) case Resource_ADJ_IN: in = true fallthrough case Resource_ADJ_OUT: - tbl, err = s.bgpServer.GetAdjRib(arg.Name, family, in, f()) + tbl, v, err = s.bgpServer.GetAdjRib(arg.Name, family, in, f()) case Resource_VRF: tbl, err = s.bgpServer.GetVrfRib(arg.Name, family, []*table.LookupPrefix{}) default: @@ -511,11 +528,13 @@ func (s *Server) GetPath(arg *GetPathRequest, stream GobgpApi_GetPathServer) err return err } + idx := 0 return func() error { for _, dst := range tbl.GetDestinations() { - for idx, path := range dst.GetAllKnownPathList() { - p := ToPathApi(path) - if idx == 0 && !table.SelectionOptions.DisableBestPathSelection { + for i, path := range dst.GetAllKnownPathList() { + p := ToPathApi(path, getValidation(v, idx)) + idx++ + if i == 0 && !table.SelectionOptions.DisableBestPathSelection { switch arg.Type { case Resource_LOCAL, Resource_GLOBAL: p.Best = true @@ -562,11 +581,11 @@ func (s *Server) MonitorRib(arg *MonitorRibRequest, stream GobgpApi_MonitorRibSe continue } if dst, y := dsts[path.GetNlri().String()]; y { - dst.Paths = append(dst.Paths, ToPathApi(path)) + dst.Paths = append(dst.Paths, ToPathApi(path, nil)) } else { dsts[path.GetNlri().String()] = &Destination{ Prefix: path.GetNlri().String(), - Paths: []*Path{ToPathApi(path)}, + Paths: []*Path{ToPathApi(path, nil)}, } } } @@ -863,7 +882,7 @@ func (s *Server) DeleteBmp(ctx context.Context, arg *DeleteBmpRequest) (*DeleteB } func (s *Server) ValidateRib(ctx context.Context, arg *ValidateRibRequest) (*ValidateRibResponse, error) { - return &ValidateRibResponse{}, s.bgpServer.ValidateRib(arg.Prefix) + return &ValidateRibResponse{}, nil } func (s *Server) AddRpki(ctx context.Context, arg *AddRpkiRequest) (*AddRpkiResponse, error) { diff --git a/client/client.go b/client/client.go index c31ac2b3..d2f56cd8 100644 --- a/client/client.go +++ b/client/client.go @@ -354,7 +354,7 @@ type AddPathByStreamClient struct { func (c *AddPathByStreamClient) Send(paths ...*table.Path) error { ps := make([]*api.Path, 0, len(paths)) for _, p := range paths { - ps = append(ps, api.ToPathApi(p)) + ps = append(ps, api.ToPathApi(p, nil)) } return c.stream.Send(&api.InjectMrtRequest{ Resource: api.Resource_GLOBAL, @@ -385,7 +385,7 @@ func (cli *Client) addPath(vrfID string, pathList []*table.Path) ([]byte, error) r, err := cli.cli.AddPath(context.Background(), &api.AddPathRequest{ Resource: resource, VrfId: vrfID, - Path: api.ToPathApi(path), + Path: api.ToPathApi(path, nil), }) if err != nil { return nil, err diff --git a/gobgp/lib/path.go b/gobgp/lib/path.go index 67cf8b06..c74a7bc0 100644 --- a/gobgp/lib/path.go +++ b/gobgp/lib/path.go @@ -58,7 +58,7 @@ func serialize_path(rf C.int, input *C.char) *C.path { return nil } path := C.new_path() - p := api.ToPathApi(pp) + p := api.ToPathApi(pp, nil) if len(p.Nlri) > 0 { path.nlri.len = C.int(len(p.Nlri)) path.nlri.value = C.CString(string(p.Nlri)) diff --git a/server/peer.go b/server/peer.go index 3bdd9965..80fc6a1b 100644 --- a/server/peer.go +++ b/server/peer.go @@ -313,105 +313,6 @@ func (peer *Peer) getAccepted(rfList []bgp.RouteFamily) []*table.Path { return peer.adjRibIn.PathList(rfList, true) } -func (peer *Peer) filterpath(path, old *table.Path) *table.Path { - // Special handling for RTM NLRI. - if path != nil && path.GetRouteFamily() == bgp.RF_RTC_UC && !path.IsWithdraw { - // If the given "path" is locally generated and the same with "old", we - // assumes "path" was already sent before. This assumption avoids the - // infinite UPDATE loop between Route Reflector and its clients. - if path.IsLocal() && path == old { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.fsm.pConf.State.NeighborAddress, - "Path": path, - }).Debug("given rtm nlri is already sent, skipping to advertise") - return nil - } - - if old != nil && old.IsLocal() { - // We assumes VRF with the specific RT is deleted. - path = old.Clone(true) - } else if peer.isRouteReflectorClient() { - // We need to send the path even if the peer is originator of the - // path in order to signal that the client should distribute route - // with the given RT. - } else { - // We send a path even if it is not the best path. See comments in - // (*Destination) GetChanges(). - dst := peer.localRib.GetDestination(path) - path = nil - for _, p := range dst.GetKnownPathList(peer.TableID(), peer.AS()) { - // Just take care not to send back. - if peer.ID() != p.GetSource().Address.String() { - path = p - break - } - } - } - } - - // only allow vpnv4 and vpnv6 paths to be advertised to VRFed neighbors. - // also check we can import this path using table.CanImportToVrf() - // if we can, make it local path by calling (*Path).ToLocal() - if path != nil && peer.fsm.pConf.Config.Vrf != "" { - if f := path.GetRouteFamily(); f != bgp.RF_IPv4_VPN && f != bgp.RF_IPv6_VPN { - return nil - } - vrf := peer.localRib.Vrfs[peer.fsm.pConf.Config.Vrf] - if table.CanImportToVrf(vrf, path) { - path = path.ToLocal() - } else { - return nil - } - } - - // replace-peer-as handling - if path != nil && !path.IsWithdraw && peer.fsm.pConf.AsPathOptions.State.ReplacePeerAs { - path = path.ReplaceAS(peer.fsm.pConf.Config.LocalAs, peer.fsm.pConf.Config.PeerAs) - } - - if path = filterpath(peer, path, old); path == nil { - return nil - } - - path = table.UpdatePathAttrs(peer.fsm.gConf, peer.fsm.pConf, peer.fsm.peerInfo, path) - - options := &table.PolicyOptions{ - Info: peer.fsm.peerInfo, - } - path = peer.policy.ApplyPolicy(peer.TableID(), table.POLICY_DIRECTION_EXPORT, path, options) - // When 'path' is filtered (path == nil), check 'old' has been sent to this peer. - // If it has, send withdrawal to the peer. - if path == nil && old != nil { - o := peer.policy.ApplyPolicy(peer.TableID(), table.POLICY_DIRECTION_EXPORT, old, options) - if o != nil { - path = old.Clone(true) - } - } - - // draft-uttaro-idr-bgp-persistence-02 - // 4.3. Processing LLGR_STALE Routes - // - // The route SHOULD NOT be advertised to any neighbor from which the - // Long-lived Graceful Restart Capability has not been received. The - // exception is described in the Optional Partial Deployment - // Procedure section (Section 4.7). Note that this requirement - // implies that such routes should be withdrawn from any such neighbor. - if path != nil && !path.IsWithdraw && !peer.isLLGREnabledFamily(path.GetRouteFamily()) && path.IsLLGRStale() { - // we send unnecessary withdrawn even if we didn't - // sent the route. - path = path.Clone(true) - } - - // remove local-pref attribute - // we should do this after applying export policy since policy may - // set local-preference - if path != nil && !peer.isIBGPPeer() && !peer.isRouteServerClient() { - path.RemoveLocalPref() - } - return path -} - func (peer *Peer) filterPathFromSourcePeer(path, old *table.Path) *table.Path { if peer.ID() != path.GetSource().Address.String() { return path @@ -447,86 +348,6 @@ func (peer *Peer) filterPathFromSourcePeer(path, old *table.Path) *table.Path { return nil } -func (peer *Peer) getBestFromLocal(rfList []bgp.RouteFamily) ([]*table.Path, []*table.Path) { - pathList := []*table.Path{} - filtered := []*table.Path{} - for _, family := range peer.toGlobalFamilies(rfList) { - pl := func() []*table.Path { - if peer.isAddPathSendEnabled(family) { - return peer.localRib.GetPathList(peer.TableID(), peer.AS(), []bgp.RouteFamily{family}) - } - return peer.localRib.GetBestPathList(peer.TableID(), peer.AS(), []bgp.RouteFamily{family}) - }() - for _, path := range pl { - if p := peer.filterpath(path, nil); p != nil { - pathList = append(pathList, p) - } else { - filtered = append(filtered, path) - } - } - } - if peer.isGracefulRestartEnabled() { - for _, family := range rfList { - pathList = append(pathList, table.NewEOR(family)) - } - } - return pathList, filtered -} - -func (peer *Peer) processOutgoingPaths(paths, olds []*table.Path) []*table.Path { - if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED { - return nil - } - if peer.fsm.pConf.GracefulRestart.State.LocalRestarting { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.fsm.pConf.State.NeighborAddress, - }).Debug("now syncing, suppress sending updates") - return nil - } - - outgoing := make([]*table.Path, 0, len(paths)) - - for idx, path := range paths { - var old *table.Path - if olds != nil { - old = olds[idx] - } - if p := peer.filterpath(path, old); p != nil { - outgoing = append(outgoing, p) - } - } - return outgoing -} - -func (peer *Peer) handleRouteRefresh(e *FsmMsg) []*table.Path { - m := e.MsgData.(*bgp.BGPMessage) - rr := m.Body.(*bgp.BGPRouteRefresh) - rf := bgp.AfiSafiToRouteFamily(rr.AFI, rr.SAFI) - if _, ok := peer.fsm.rfMap[rf]; !ok { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.ID(), - "Data": rf, - }).Warn("Route family isn't supported") - return nil - } - if _, ok := peer.fsm.capMap[bgp.BGP_CAP_ROUTE_REFRESH]; !ok { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.ID(), - }).Warn("ROUTE_REFRESH received but the capability wasn't advertised") - return nil - } - rfList := []bgp.RouteFamily{rf} - accepted, filtered := peer.getBestFromLocal(rfList) - for _, path := range filtered { - path.IsWithdraw = true - accepted = append(accepted, path) - } - return accepted -} - func (peer *Peer) doPrefixLimit(k bgp.RouteFamily, c *config.PrefixLimitConfig) *bgp.BGPMessage { if maxPrefixes := int(c.MaxPrefixes); maxPrefixes > 0 { count := peer.adjRibIn.Count([]bgp.RouteFamily{k}) @@ -652,58 +473,6 @@ func (peer *Peer) PassConn(conn *net.TCPConn) { } } -func (peer *Peer) ToConfig(getAdvertised bool) *config.Neighbor { - // create copy which can be access to without mutex - conf := *peer.fsm.pConf - - conf.AfiSafis = make([]config.AfiSafi, len(peer.fsm.pConf.AfiSafis)) - for i, af := range peer.fsm.pConf.AfiSafis { - conf.AfiSafis[i] = af - conf.AfiSafis[i].AddPaths.State.Receive = peer.isAddPathReceiveEnabled(af.State.Family) - if peer.isAddPathSendEnabled(af.State.Family) { - conf.AfiSafis[i].AddPaths.State.SendMax = af.AddPaths.State.SendMax - } else { - conf.AfiSafis[i].AddPaths.State.SendMax = 0 - } - } - - remoteCap := make([]bgp.ParameterCapabilityInterface, 0, len(peer.fsm.capMap)) - for _, caps := range peer.fsm.capMap { - for _, m := range caps { - // need to copy all values here - buf, _ := m.Serialize() - c, _ := bgp.DecodeCapability(buf) - remoteCap = append(remoteCap, c) - } - } - conf.State.RemoteCapabilityList = remoteCap - conf.State.LocalCapabilityList = capabilitiesFromConfig(peer.fsm.pConf) - - conf.State.SessionState = config.IntToSessionStateMap[int(peer.fsm.state)] - conf.State.AdminState = config.IntToAdminStateMap[int(peer.fsm.adminState)] - - if peer.fsm.state == bgp.BGP_FSM_ESTABLISHED { - rfList := peer.configuredRFlist() - if getAdvertised { - pathList, filtered := peer.getBestFromLocal(rfList) - conf.State.AdjTable.Advertised = uint32(len(pathList)) - conf.State.AdjTable.Filtered = uint32(len(filtered)) - } else { - conf.State.AdjTable.Advertised = 0 - } - conf.State.AdjTable.Received = uint32(peer.adjRibIn.Count(rfList)) - conf.State.AdjTable.Accepted = uint32(peer.adjRibIn.Accepted(rfList)) - - conf.Transport.State.LocalAddress, conf.Transport.State.LocalPort = peer.fsm.LocalHostPort() - _, conf.Transport.State.RemotePort = peer.fsm.RemoteHostPort() - buf, _ := peer.fsm.recvOpen.Serialize() - // need to copy all values here - conf.State.ReceivedOpenMessage, _ = bgp.ParseBGPMessage(buf) - conf.State.RemoteRouterId = peer.fsm.peerInfo.ID.To4().String() - } - return &conf -} - func (peer *Peer) DropAll(rfList []bgp.RouteFamily) { peer.adjRibIn.Drop(rfList) } diff --git a/server/rpki.go b/server/rpki.go index 371fda4b..6356888a 100644 --- a/server/rpki.go +++ b/server/rpki.go @@ -112,6 +112,10 @@ func NewROAManager(as uint32) (*roaManager, error) { return m, nil } +func (c *roaManager) enabled() bool { + return len(c.clientMap) != 0 +} + func (m *roaManager) SetAS(as uint32) error { if m.AS != 0 { return fmt.Errorf("AS was already configured") @@ -570,21 +574,15 @@ func ValidatePath(ownAs uint32, tree *radix.Tree, cidr string, asPath *bgp.PathA return validation } -func (c *roaManager) validate(pathList []*table.Path) { - if len(c.clientMap) == 0 { - // RPKI isn't enabled - return +func (c *roaManager) validate(path *table.Path) *table.Validation { + if len(c.clientMap) == 0 || path.IsWithdraw || path.IsEOR() { + // RPKI isn't enabled or invalid path + return nil } - - for _, path := range pathList { - if path.IsWithdraw || path.IsEOR() { - continue - } - if tree, ok := c.Roas[path.GetRouteFamily()]; ok { - v := ValidatePath(c.AS, tree, path.GetNlri().String(), path.GetAsPath()) - path.SetValidation(v) - } + if tree, ok := c.Roas[path.GetRouteFamily()]; ok { + return ValidatePath(c.AS, tree, path.GetNlri().String(), path.GetAsPath()) } + return nil } type roaClient struct { diff --git a/server/server.go b/server/server.go index 75d71a6b..3c7bd336 100644 --- a/server/server.go +++ b/server/server.go @@ -463,6 +463,109 @@ func filterpath(peer *Peer, path, old *table.Path) *table.Path { return path } +func (s *BgpServer) filterpath(peer *Peer, path, old *table.Path) *table.Path { + // Special handling for RTM NLRI. + if path != nil && path.GetRouteFamily() == bgp.RF_RTC_UC && !path.IsWithdraw { + // If the given "path" is locally generated and the same with "old", we + // assumes "path" was already sent before. This assumption avoids the + // infinite UPDATE loop between Route Reflector and its clients. + if path.IsLocal() && path == old { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": peer.fsm.pConf.State.NeighborAddress, + "Path": path, + }).Debug("given rtm nlri is already sent, skipping to advertise") + return nil + } + + if old != nil && old.IsLocal() { + // We assumes VRF with the specific RT is deleted. + path = old.Clone(true) + } else if peer.isRouteReflectorClient() { + // We need to send the path even if the peer is originator of the + // path in order to signal that the client should distribute route + // with the given RT. + } else { + // We send a path even if it is not the best path. See comments in + // (*Destination) GetChanges(). + dst := peer.localRib.GetDestination(path) + path = nil + for _, p := range dst.GetKnownPathList(peer.TableID(), peer.AS()) { + // Just take care not to send back. + if peer.ID() != p.GetSource().Address.String() { + path = p + break + } + } + } + } + + // only allow vpnv4 and vpnv6 paths to be advertised to VRFed neighbors. + // also check we can import this path using table.CanImportToVrf() + // if we can, make it local path by calling (*Path).ToLocal() + if path != nil && peer.fsm.pConf.Config.Vrf != "" { + if f := path.GetRouteFamily(); f != bgp.RF_IPv4_VPN && f != bgp.RF_IPv6_VPN { + return nil + } + vrf := peer.localRib.Vrfs[peer.fsm.pConf.Config.Vrf] + if table.CanImportToVrf(vrf, path) { + path = path.ToLocal() + } else { + return nil + } + } + + // replace-peer-as handling + if path != nil && !path.IsWithdraw && peer.fsm.pConf.AsPathOptions.State.ReplacePeerAs { + path = path.ReplaceAS(peer.fsm.pConf.Config.LocalAs, peer.fsm.pConf.Config.PeerAs) + } + + if path = filterpath(peer, path, old); path == nil { + return nil + } + + path = table.UpdatePathAttrs(peer.fsm.gConf, peer.fsm.pConf, peer.fsm.peerInfo, path) + + options := &table.PolicyOptions{ + Info: peer.fsm.peerInfo, + } + if v := s.roaManager.validate(path); v != nil { + options.ValidationResult = v + } + + path = peer.policy.ApplyPolicy(peer.TableID(), table.POLICY_DIRECTION_EXPORT, path, options) + // When 'path' is filtered (path == nil), check 'old' has been sent to this peer. + // If it has, send withdrawal to the peer. + if path == nil && old != nil { + o := peer.policy.ApplyPolicy(peer.TableID(), table.POLICY_DIRECTION_EXPORT, old, options) + if o != nil { + path = old.Clone(true) + } + } + + // draft-uttaro-idr-bgp-persistence-02 + // 4.3. Processing LLGR_STALE Routes + // + // The route SHOULD NOT be advertised to any neighbor from which the + // Long-lived Graceful Restart Capability has not been received. The + // exception is described in the Optional Partial Deployment + // Procedure section (Section 4.7). Note that this requirement + // implies that such routes should be withdrawn from any such neighbor. + if path != nil && !path.IsWithdraw && !peer.isLLGREnabledFamily(path.GetRouteFamily()) && path.IsLLGRStale() { + // we send unnecessary withdrawn even if we didn't + // sent the route. + path = path.Clone(true) + } + + // remove local-pref attribute + // we should do this after applying export policy since policy may + // set local-preference + if path != nil && !peer.isIBGPPeer() && !peer.isRouteServerClient() { + path.RemoveLocalPref() + } + return path +} + func clonePathList(pathList []*table.Path) []*table.Path { l := make([]*table.Path, 0, len(pathList)) for _, p := range pathList { @@ -501,6 +604,58 @@ func (server *BgpServer) notifyBestWatcher(best []*table.Path, multipath [][]*ta server.notifyWatcher(WATCH_EVENT_TYPE_BEST_PATH, w) } +func (s *BgpServer) ToConfig(peer *Peer, getAdvertised bool) *config.Neighbor { + // create copy which can be access to without mutex + conf := *peer.fsm.pConf + + conf.AfiSafis = make([]config.AfiSafi, len(peer.fsm.pConf.AfiSafis)) + for i, af := range peer.fsm.pConf.AfiSafis { + conf.AfiSafis[i] = af + conf.AfiSafis[i].AddPaths.State.Receive = peer.isAddPathReceiveEnabled(af.State.Family) + if peer.isAddPathSendEnabled(af.State.Family) { + conf.AfiSafis[i].AddPaths.State.SendMax = af.AddPaths.State.SendMax + } else { + conf.AfiSafis[i].AddPaths.State.SendMax = 0 + } + } + + remoteCap := make([]bgp.ParameterCapabilityInterface, 0, len(peer.fsm.capMap)) + for _, caps := range peer.fsm.capMap { + for _, m := range caps { + // need to copy all values here + buf, _ := m.Serialize() + c, _ := bgp.DecodeCapability(buf) + remoteCap = append(remoteCap, c) + } + } + conf.State.RemoteCapabilityList = remoteCap + conf.State.LocalCapabilityList = capabilitiesFromConfig(peer.fsm.pConf) + + conf.State.SessionState = config.IntToSessionStateMap[int(peer.fsm.state)] + conf.State.AdminState = config.IntToAdminStateMap[int(peer.fsm.adminState)] + + if peer.fsm.state == bgp.BGP_FSM_ESTABLISHED { + rfList := peer.configuredRFlist() + if getAdvertised { + pathList, filtered := s.getBestFromLocal(peer, rfList) + conf.State.AdjTable.Advertised = uint32(len(pathList)) + conf.State.AdjTable.Filtered = uint32(len(filtered)) + } else { + conf.State.AdjTable.Advertised = 0 + } + conf.State.AdjTable.Received = uint32(peer.adjRibIn.Count(rfList)) + conf.State.AdjTable.Accepted = uint32(peer.adjRibIn.Accepted(rfList)) + + conf.Transport.State.LocalAddress, conf.Transport.State.LocalPort = peer.fsm.LocalHostPort() + _, conf.Transport.State.RemotePort = peer.fsm.RemoteHostPort() + buf, _ := peer.fsm.recvOpen.Serialize() + // need to copy all values here + conf.State.ReceivedOpenMessage, _ = bgp.ParseBGPMessage(buf) + conf.State.RemoteRouterId = peer.fsm.peerInfo.ID.To4().String() + } + return &conf +} + func (server *BgpServer) notifyPrePolicyUpdateWatcher(peer *Peer, pathList []*table.Path, msg *bgp.BGPMessage, timestamp time.Time, payload []byte) { if !server.isWatched(WATCH_EVENT_TYPE_PRE_UPDATE) || peer == nil { return @@ -523,7 +678,7 @@ func (server *BgpServer) notifyPrePolicyUpdateWatcher(peer *Peer, pathList []*ta Payload: payload, PostPolicy: false, PathList: cloned, - Neighbor: peer.ToConfig(false), + Neighbor: server.ToConfig(peer, false), } server.notifyWatcher(WATCH_EVENT_TYPE_PRE_UPDATE, ev) } @@ -548,7 +703,7 @@ func (server *BgpServer) notifyPostPolicyUpdateWatcher(peer *Peer, pathList []*t Timestamp: cloned[0].GetTimestamp(), PostPolicy: true, PathList: cloned, - Neighbor: peer.ToConfig(false), + Neighbor: server.ToConfig(peer, false), } server.notifyWatcher(WATCH_EVENT_TYPE_POST_UPDATE, ev) } @@ -609,17 +764,89 @@ func (server *BgpServer) notifyRecvMessageWatcher(peer *Peer, timestamp time.Tim server.notifyMessageWatcher(peer, timestamp, msg, false) } +func (s *BgpServer) getBestFromLocal(peer *Peer, rfList []bgp.RouteFamily) ([]*table.Path, []*table.Path) { + pathList := []*table.Path{} + filtered := []*table.Path{} + for _, family := range peer.toGlobalFamilies(rfList) { + pl := func() []*table.Path { + if peer.isAddPathSendEnabled(family) { + return peer.localRib.GetPathList(peer.TableID(), peer.AS(), []bgp.RouteFamily{family}) + } + return peer.localRib.GetBestPathList(peer.TableID(), peer.AS(), []bgp.RouteFamily{family}) + }() + for _, path := range pl { + if p := s.filterpath(peer, path, nil); p != nil { + pathList = append(pathList, p) + } else { + filtered = append(filtered, path) + } + } + } + if peer.isGracefulRestartEnabled() { + for _, family := range rfList { + pathList = append(pathList, table.NewEOR(family)) + } + } + return pathList, filtered +} + +func (s *BgpServer) processOutgoingPaths(peer *Peer, paths, olds []*table.Path) []*table.Path { + if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED { + return nil + } + if peer.fsm.pConf.GracefulRestart.State.LocalRestarting { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": peer.fsm.pConf.State.NeighborAddress, + }).Debug("now syncing, suppress sending updates") + return nil + } + + outgoing := make([]*table.Path, 0, len(paths)) + + for idx, path := range paths { + var old *table.Path + if olds != nil { + old = olds[idx] + } + if p := s.filterpath(peer, path, old); p != nil { + outgoing = append(outgoing, p) + } + } + return outgoing +} + +func (s *BgpServer) handleRouteRefresh(peer *Peer, e *FsmMsg) []*table.Path { + m := e.MsgData.(*bgp.BGPMessage) + rr := m.Body.(*bgp.BGPRouteRefresh) + rf := bgp.AfiSafiToRouteFamily(rr.AFI, rr.SAFI) + if _, ok := peer.fsm.rfMap[rf]; !ok { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": peer.ID(), + "Data": rf, + }).Warn("Route family isn't supported") + return nil + } + if _, ok := peer.fsm.capMap[bgp.BGP_CAP_ROUTE_REFRESH]; !ok { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": peer.ID(), + }).Warn("ROUTE_REFRESH received but the capability wasn't advertised") + return nil + } + rfList := []bgp.RouteFamily{rf} + accepted, filtered := s.getBestFromLocal(peer, rfList) + for _, path := range filtered { + path.IsWithdraw = true + accepted = append(accepted, path) + } + return accepted +} + func (server *BgpServer) propagateUpdate(peer *Peer, pathList []*table.Path) { rs := peer != nil && peer.isRouteServerClient() vrf := !rs && peer != nil && peer.fsm.pConf.Config.Vrf != "" - var policyOptions *table.PolicyOptions - if !rs && peer != nil { - policyOptions = &table.PolicyOptions{ - Info: peer.fsm.peerInfo, - } - } else { - policyOptions = nil - } tableId := table.GLOBAL_RIB_NAME rib := server.globalRib @@ -633,6 +860,15 @@ func (server *BgpServer) propagateUpdate(peer *Peer, pathList []*table.Path) { path = path.ToGlobal(rib.Vrfs[peer.fsm.pConf.Config.Vrf]) } + policyOptions := &table.PolicyOptions{} + + if !rs && peer != nil { + policyOptions.Info = peer.fsm.peerInfo + } + if v := server.roaManager.validate(path); v != nil { + policyOptions.ValidationResult = v + } + if p := server.policy.ApplyPolicy(tableId, table.POLICY_DIRECTION_IMPORT, path, policyOptions); p != nil { path = p } else { @@ -663,7 +899,7 @@ func (server *BgpServer) propagateUpdate(peer *Peer, pathList []*table.Path) { } var candidates []*table.Path if path.IsWithdraw { - candidates, _ = peer.getBestFromLocal(peer.configuredRFlist()) + candidates, _ = server.getBestFromLocal(peer, peer.configuredRFlist()) } else { candidates = server.globalRib.GetBestPathList(peer.TableID(), 0, fs) } @@ -680,9 +916,9 @@ func (server *BgpServer) propagateUpdate(peer *Peer, pathList []*table.Path) { } } if path.IsWithdraw { - paths = peer.processOutgoingPaths(nil, paths) + paths = server.processOutgoingPaths(peer, nil, paths) } else { - paths = peer.processOutgoingPaths(paths, nil) + paths = server.processOutgoingPaths(peer, paths, nil) } sendFsmOutgoingMsg(peer, paths, nil, false) } @@ -752,7 +988,7 @@ func (server *BgpServer) propagateUpdateToNeighbors(source *Peer, newPath *table if !needOld { oldList = nil } - if paths := targetPeer.processOutgoingPaths(bestList, oldList); len(paths) > 0 { + if paths := server.processOutgoingPaths(targetPeer, bestList, oldList); len(paths) > 0 { sendFsmOutgoingMsg(targetPeer, paths, nil, false) } } @@ -892,7 +1128,7 @@ func (server *BgpServer) handleFSMMessage(peer *Peer, e *FsmMsg) { var pathList []*table.Path _, y := peer.fsm.rfMap[bgp.RF_RTC_UC] if c := peer.fsm.pConf.GetAfiSafi(bgp.RF_RTC_UC); y && !peer.fsm.pConf.GracefulRestart.State.PeerRestarting && c.RouteTargetMembership.Config.DeferralTime > 0 { - pathList, _ = peer.getBestFromLocal([]bgp.RouteFamily{bgp.RF_RTC_UC}) + pathList, _ = server.getBestFromLocal(peer, []bgp.RouteFamily{bgp.RF_RTC_UC}) t := c.RouteTargetMembership.Config.DeferralTime for _, f := range peer.configuredRFlist() { if f != bgp.RF_RTC_UC { @@ -900,7 +1136,7 @@ func (server *BgpServer) handleFSMMessage(peer *Peer, e *FsmMsg) { } } } else { - pathList, _ = peer.getBestFromLocal(peer.configuredRFlist()) + pathList, _ = server.getBestFromLocal(peer, peer.configuredRFlist()) } if len(pathList) > 0 { @@ -948,7 +1184,7 @@ func (server *BgpServer) handleFSMMessage(peer *Peer, e *FsmMsg) { if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED || e.timestamp.Unix() < peer.fsm.pConf.Timers.State.Uptime { return } - if paths := peer.handleRouteRefresh(e); len(paths) > 0 { + if paths := server.handleRouteRefresh(peer, e); len(paths) > 0 { sendFsmOutgoingMsg(peer, paths, nil, false) return } @@ -962,7 +1198,6 @@ func (server *BgpServer) handleFSMMessage(peer *Peer, e *FsmMsg) { if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED || e.timestamp.Unix() < peer.fsm.pConf.Timers.State.Uptime { return } - server.roaManager.validate(e.PathList) pathList, eor, notification := peer.handleUpdate(e) if notification != nil { sendFsmOutgoingMsg(peer, nil, notification, true) @@ -1011,7 +1246,7 @@ func (server *BgpServer) handleFSMMessage(peer *Peer, e *FsmMsg) { if !p.isGracefulRestartEnabled() { continue } - paths, _ := p.getBestFromLocal(p.configuredRFlist()) + paths, _ := server.getBestFromLocal(p, p.configuredRFlist()) if len(paths) > 0 { sendFsmOutgoingMsg(p, paths, nil, false) } @@ -1053,7 +1288,7 @@ func (server *BgpServer) handleFSMMessage(peer *Peer, e *FsmMsg) { families = append(families, f) } } - if paths, _ := peer.getBestFromLocal(families); len(paths) > 0 { + if paths, _ := server.getBestFromLocal(peer, families); len(paths) > 0 { sendFsmOutgoingMsg(peer, paths, nil, false) } } @@ -1501,7 +1736,7 @@ func (s *BgpServer) softResetOut(addr string, family bgp.RouteFamily, deferral b } } - pathList, filtered := peer.getBestFromLocal(families) + pathList, filtered := s.getBestFromLocal(peer, families) if len(pathList) > 0 { sendFsmOutgoingMsg(peer, pathList, nil, false) } @@ -1550,7 +1785,19 @@ func (s *BgpServer) SoftReset(addr string, family bgp.RouteFamily) error { }, true) } -func (s *BgpServer) GetRib(addr string, family bgp.RouteFamily, prefixes []*table.LookupPrefix) (rib *table.Table, err error) { +func (s *BgpServer) validateTable(r *table.Table) (v []*table.Validation) { + if s.roaManager.enabled() { + v := make([]*table.Validation, 0, len(r.GetDestinations())) + for _, d := range r.GetDestinations() { + for _, p := range d.GetAllKnownPathList() { + v = append(v, s.roaManager.validate(p)) + } + } + } + return +} + +func (s *BgpServer) GetRib(addr string, family bgp.RouteFamily, prefixes []*table.LookupPrefix) (rib *table.Table, v []*table.Validation, err error) { err = s.mgmtOperation(func() error { m := s.globalRib id := table.GLOBAL_RIB_NAME @@ -1573,6 +1820,7 @@ func (s *BgpServer) GetRib(addr string, family bgp.RouteFamily, prefixes []*tabl return fmt.Errorf("address family: %s not supported", af) } rib, err = tbl.Select(table.TableSelectOption{ID: id, AS: as, LookupPrefixes: prefixes}) + v = s.validateTable(rib) return err }, true) return @@ -1604,7 +1852,7 @@ func (s *BgpServer) GetVrfRib(name string, family bgp.RouteFamily, prefixes []*t return } -func (s *BgpServer) GetAdjRib(addr string, family bgp.RouteFamily, in bool, prefixes []*table.LookupPrefix) (rib *table.Table, err error) { +func (s *BgpServer) GetAdjRib(addr string, family bgp.RouteFamily, in bool, prefixes []*table.LookupPrefix) (rib *table.Table, v []*table.Validation, err error) { err = s.mgmtOperation(func() error { peer, ok := s.neighborMap[addr] if !ok { @@ -1618,10 +1866,11 @@ func (s *BgpServer) GetAdjRib(addr string, family bgp.RouteFamily, in bool, pref adjRib = peer.adjRibIn } else { adjRib = table.NewAdjRib(id, peer.configuredRFlist()) - accepted, _ := peer.getBestFromLocal(peer.configuredRFlist()) + accepted, _ := s.getBestFromLocal(peer, peer.configuredRFlist()) adjRib.Update(accepted) } rib, err = adjRib.Select(family, false, table.TableSelectOption{ID: id, AS: as, LookupPrefixes: prefixes}) + v = s.validateTable(rib) return err }, true) return @@ -1662,7 +1911,7 @@ func (s *BgpServer) GetAdjRibInfo(addr string, family bgp.RouteFamily, in bool) adjRib = peer.adjRibIn } else { adjRib = table.NewAdjRib(peer.ID(), peer.configuredRFlist()) - accepted, _ := peer.getBestFromLocal(peer.configuredRFlist()) + accepted, _ := s.getBestFromLocal(peer, peer.configuredRFlist()) adjRib.Update(accepted) } info, err = adjRib.TableInfo(family) @@ -1687,7 +1936,7 @@ func (s *BgpServer) GetNeighbor(address string, getAdvertised bool) (l []*config if address != "" && address != k && address != peer.fsm.pConf.Config.NeighborInterface { continue } - l = append(l, peer.ToConfig(getAdvertised)) + l = append(l, s.ToConfig(peer, getAdvertised)) } return nil }, false) @@ -2259,32 +2508,6 @@ func (s *BgpServer) DisableMrt(c *config.MrtConfig) error { }, false) } -func (s *BgpServer) ValidateRib(prefix string) error { - return s.mgmtOperation(func() error { - for _, rf := range s.globalRib.GetRFlist() { - if t, ok := s.globalRib.Tables[rf]; ok { - dsts := t.GetDestinations() - if prefix != "" { - addr, _, _ := net.ParseCIDR(prefix) - var nlri bgp.AddrPrefixInterface - if addr.To16() == nil { - nlri, _ = bgp.NewPrefixFromRouteFamily(bgp.AFI_IP, bgp.SAFI_UNICAST, prefix) - } else { - nlri, _ = bgp.NewPrefixFromRouteFamily(bgp.AFI_IP6, bgp.SAFI_UNICAST, prefix) - } - if dst := t.GetDestination(nlri); dst != nil { - dsts = map[string]*table.Destination{nlri.String(): dst} - } - } - for _, dst := range dsts { - s.roaManager.validate(dst.GetAllKnownPathList()) - } - } - } - return nil - }, true) -} - func (s *BgpServer) GetRpki() (l []*config.RpkiServer, err error) { err = s.mgmtOperation(func() error { l = s.roaManager.GetServers() @@ -2531,7 +2754,7 @@ func (w *Watcher) Generate(t WatchEventType) error { }() l := make([]*config.Neighbor, 0, len(w.s.neighborMap)) for _, peer := range w.s.neighborMap { - l = append(l, peer.ToConfig(false)) + l = append(l, w.s.ToConfig(peer, false)) } w.notify(&WatchEventTable{PathList: pathList, Neighbor: l}) default: @@ -2635,7 +2858,7 @@ func (s *BgpServer) Watch(opts ...WatchOption) (w *Watcher) { if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED { continue } - configNeighbor := peer.ToConfig(false) + configNeighbor := w.s.ToConfig(peer, false) for _, rf := range peer.configuredRFlist() { _, y := peer.fsm.capMap[bgp.BGP_CAP_FOUR_OCTET_AS_NUMBER] l, _ := peer.fsm.LocalHostPort() @@ -2688,7 +2911,7 @@ func (s *BgpServer) Watch(opts ...WatchOption) (w *Watcher) { // create copy which can be access to without mutex var configNeighbor *config.Neighbor if peer, ok := s.neighborMap[peerInfo.Address.String()]; ok { - configNeighbor = peer.ToConfig(false) + configNeighbor = w.s.ToConfig(peer, false) } for _, path := range paths { msgs := table.CreateUpdateMsgFromPaths([]*table.Path{path}) diff --git a/server/server_test.go b/server/server_test.go index 0bbd4070..7f854e73 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -162,7 +162,7 @@ func TestMonitor(test *testing.T) { } for { // Waits for the initial route will be advertised. - rib, err := s.GetRib("", bgp.RF_IPv4_UC, nil) + rib, _, err := s.GetRib("", bgp.RF_IPv4_UC, nil) if err != nil { log.Fatal(err) } @@ -407,7 +407,8 @@ func TestFilterpathWithRejectPolicy(t *testing.T) { path1 := table.NewPath(pi1, nlri, false, pa1, time.Now(), false) new, old := process(rib2, []*table.Path{path1}) assert.Equal(t, new, path1) - path2 := p2.filterpath(new, old) + s := NewBgpServer() + path2 := s.filterpath(p2, new, old) if addCommunity { assert.True(t, path2.IsWithdraw) } else { diff --git a/server/zclient.go b/server/zclient.go index 12f5357f..eb3e0b81 100644 --- a/server/zclient.go +++ b/server/zclient.go @@ -471,7 +471,7 @@ func (z *zebraClient) loop() { Tables: make(map[bgp.RouteFamily]*table.Table), } for _, rf := range rfListFromNexthopUpdateBody(body) { - rib, err := z.server.GetRib("", rf, nil) + rib, _, err := z.server.GetRib("", rf, nil) if err != nil { log.Errorf("failed to get global rib by family %s", rf.String()) continue diff --git a/table/destination.go b/table/destination.go index 84183002..e063d89d 100644 --- a/table/destination.go +++ b/table/destination.go @@ -968,7 +968,7 @@ func (d *Destination) MarshalJSON() ([]byte, error) { return json.Marshal(d.GetAllKnownPathList()) } -func (old *Destination) Select(option ...DestinationSelectOption) *Destination { +func (d *Destination) Select(option ...DestinationSelectOption) *Destination { id := GLOBAL_RIB_NAME var vrf *Vrf adj := false @@ -989,9 +989,10 @@ func (old *Destination) Select(option ...DestinationSelectOption) *Destination { } var paths []*Path if adj { - paths = old.knownPathList + paths = make([]*Path, len(d.knownPathList)) + copy(paths, d.knownPathList) } else { - paths = old.GetKnownPathList(id, as) + paths = d.GetKnownPathList(id, as) if vrf != nil { ps := make([]*Path, 0, len(paths)) for _, p := range paths { @@ -1022,12 +1023,7 @@ func (old *Destination) Select(option ...DestinationSelectOption) *Destination { } } } - new := NewDestination(old.nlri, 0) - for _, path := range paths { - p := path.Clone(path.IsWithdraw) - new.knownPathList = append(new.knownPathList, p) - } - return new + return NewDestination(d.nlri, 0, paths...) } type destinations []*Destination diff --git a/table/policy.go b/table/policy.go index 299a976b..e42c3462 100644 --- a/table/policy.go +++ b/table/policy.go @@ -35,7 +35,8 @@ import ( ) type PolicyOptions struct { - Info *PeerInfo + Info *PeerInfo + ValidationResult *Validation } type DefinedType int @@ -1712,8 +1713,11 @@ func (c *RpkiValidationCondition) Type() ConditionType { return CONDITION_RPKI } -func (c *RpkiValidationCondition) Evaluate(path *Path, _ *PolicyOptions) bool { - return c.result == path.ValidationStatus() +func (c *RpkiValidationCondition) Evaluate(path *Path, options *PolicyOptions) bool { + if options != nil && options.ValidationResult != nil { + return c.result == options.ValidationResult.Status + } + return false } func (c *RpkiValidationCondition) Set() DefinedSet { |