diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/fsm.go | 75 | ||||
-rw-r--r-- | server/fsm_test.go | 9 | ||||
-rw-r--r-- | server/server.go | 35 |
3 files changed, 91 insertions, 28 deletions
diff --git a/server/fsm.go b/server/fsm.go index f623f399..e1b85cd0 100644 --- a/server/fsm.go +++ b/server/fsm.go @@ -600,6 +600,30 @@ func readAll(conn net.Conn, length int) ([]byte, error) { return buf, nil } +func getPathAttrFromBGPUpdate(m *bgp.BGPUpdate, typ bgp.BGPAttrType) bgp.PathAttributeInterface { + for _, a := range m.PathAttributes { + if a.GetType() == typ { + return a + } + } + return nil +} + +func hasOwnASLoop(ownAS uint32, limit int, aspath *bgp.PathAttributeAsPath) bool { + cnt := 0 + for _, i := range aspath.Value { + for _, as := range i.(*bgp.As4PathParam).AS { + if as == ownAS { + cnt++ + if cnt > limit { + return true + } + } + } + } + return false +} + func (h *FSMHandler) recvMessageWithError() (*FsmMsg, error) { sendToErrorCh := func(reason FsmStateReason) { // probably doesn't happen but be cautious @@ -671,6 +695,11 @@ func (h *FSMHandler) recvMessageWithError() (*FsmMsg, error) { case bgp.BGP_MSG_UPDATE: body := m.Body.(*bgp.BGPUpdate) confedCheck := !config.IsConfederationMember(h.fsm.gConf, h.fsm.pConf) && config.IsEBGPPeer(h.fsm.gConf, h.fsm.pConf) + + fmsg.payload = make([]byte, len(headerBuf)+len(bodyBuf)) + copy(fmsg.payload, headerBuf) + copy(fmsg.payload[len(headerBuf):], bodyBuf) + _, err = bgp.ValidateUpdateMsg(body, h.fsm.rfMap, confedCheck) if err != nil { log.WithFields(log.Fields{ @@ -680,28 +709,34 @@ func (h *FSMHandler) recvMessageWithError() (*FsmMsg, error) { "error": err, }).Warn("malformed BGP update message") fmsg.MsgData = err - } else { - // FIXME: we should use the original message for bmp/mrt - table.UpdatePathAttrs4ByteAs(body) - err = table.UpdatePathAggregator4ByteAs(body) - if err == nil { - fmsg.PathList = table.ProcessMessage(m, h.fsm.peerInfo, fmsg.timestamp) - id := h.fsm.pConf.Config.NeighborAddress - for _, path := range fmsg.PathList { - if path.IsEOR() { - continue - } - if h.fsm.policy.ApplyPolicy(id, table.POLICY_DIRECTION_IN, path, nil) == nil { - path.Filter(id, table.POLICY_DIRECTION_IN) - } - } - } else { - fmsg.MsgData = err + return fmsg, err + } + + table.UpdatePathAttrs4ByteAs(body) + if err = table.UpdatePathAggregator4ByteAs(body); err != nil { + fmsg.MsgData = err + return fmsg, err + } + + // RFC4271 9.1.2 Phase 2: Route Selection + // + // If the AS_PATH attribute of a BGP route contains an AS loop, the BGP + // route should be excluded from the Phase 2 decision function. + var asLoop bool + if attr := getPathAttrFromBGPUpdate(body, bgp.BGP_ATTR_TYPE_AS_PATH); attr != nil { + asLoop = hasOwnASLoop(h.fsm.peerInfo.LocalAS, int(h.fsm.pConf.AsPathOptions.Config.AllowOwnAs), attr.(*bgp.PathAttributeAsPath)) + } + + fmsg.PathList = table.ProcessMessage(m, h.fsm.peerInfo, fmsg.timestamp) + id := h.fsm.pConf.Config.NeighborAddress + for _, path := range fmsg.PathList { + if path.IsEOR() { + continue + } + if asLoop || (h.fsm.policy.ApplyPolicy(id, table.POLICY_DIRECTION_IN, path, nil) == nil) { + path.Filter(id, table.POLICY_DIRECTION_IN) } } - fmsg.payload = make([]byte, len(headerBuf)+len(bodyBuf)) - copy(fmsg.payload, headerBuf) - copy(fmsg.payload[len(headerBuf):], bodyBuf) fallthrough case bgp.BGP_MSG_KEEPALIVE: // if the length of h.holdTimerResetCh diff --git a/server/fsm_test.go b/server/fsm_test.go index 0fb67f98..aea6700b 100644 --- a/server/fsm_test.go +++ b/server/fsm_test.go @@ -287,6 +287,15 @@ func TestFSMHandlerEstablished_HoldtimeZero(t *testing.T) { assert.Equal(0, len(m.sendBuf)) } +func TestCheckOwnASLoop(t *testing.T) { + assert := assert.New(t) + aspathParam := []bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, []uint32{65100})} + aspath := bgp.NewPathAttributeAsPath(aspathParam) + assert.False(hasOwnASLoop(65100, 10, aspath)) + assert.True(hasOwnASLoop(65100, 0, aspath)) + assert.False(hasOwnASLoop(65200, 0, aspath)) +} + func makePeerAndHandler() (*Peer, *FSMHandler) { p := &Peer{ fsm: NewFSM(&config.Global{}, &config.Neighbor{}, table.NewRoutingPolicy()), diff --git a/server/server.go b/server/server.go index f3861de3..6e20979b 100644 --- a/server/server.go +++ b/server/server.go @@ -1317,7 +1317,17 @@ func (s *BgpServer) softResetIn(addr string, family bgp.RouteFamily) error { for _, path := range peer.adjRibIn.PathList(families, false) { exResult := path.Filtered(peer.ID()) path.Filter(peer.ID(), table.POLICY_DIRECTION_NONE) - if s.policy.ApplyPolicy(peer.ID(), table.POLICY_DIRECTION_IN, path, nil) != nil { + + // RFC4271 9.1.2 Phase 2: Route Selection + // + // If the AS_PATH attribute of a BGP route contains an AS loop, the BGP + // route should be excluded from the Phase 2 decision function. + var asLoop bool + if aspath := path.GetAsPath(); aspath != nil { + asLoop = hasOwnASLoop(peer.fsm.peerInfo.LocalAS, int(peer.fsm.pConf.AsPathOptions.Config.AllowOwnAs), aspath) + } + + if !asLoop && s.policy.ApplyPolicy(peer.ID(), table.POLICY_DIRECTION_IN, path, nil) != nil { pathList = append(pathList, path.Clone(false)) // this path still in rib's // knownPathList. We can't @@ -1482,13 +1492,13 @@ func (s *BgpServer) GetAdjRib(addr string, family bgp.RouteFamily, in bool, pref if !ok { return fmt.Errorf("Neighbor that has %v doesn't exist.", addr) } - id := peer.TableID() + id := peer.ID() var adjRib *table.AdjRib if in { adjRib = peer.adjRibIn } else { - adjRib = table.NewAdjRib(peer.ID(), peer.configuredRFlist()) + adjRib = table.NewAdjRib(id, peer.configuredRFlist()) accepted, _ := peer.getBestFromLocal(peer.configuredRFlist()) adjRib.Update(accepted) } @@ -1707,7 +1717,7 @@ func (s *BgpServer) DeleteNeighbor(c *config.Neighbor) error { }, true) } -func (s *BgpServer) UpdateNeighbor(c *config.Neighbor) (policyUpdated bool, err error) { +func (s *BgpServer) UpdateNeighbor(c *config.Neighbor) (needsSoftResetIn bool, err error) { err = s.mgmtOperation(func() error { addr := c.Config.NeighborAddress peer, ok := s.neighborMap[addr] @@ -1722,10 +1732,19 @@ func (s *BgpServer) UpdateNeighbor(c *config.Neighbor) (policyUpdated bool, err }).Info("Update ApplyPolicy") s.policy.Reset(nil, map[string]config.ApplyPolicy{peer.ID(): c.ApplyPolicy}) peer.fsm.pConf.ApplyPolicy = c.ApplyPolicy - policyUpdated = true + needsSoftResetIn = true } original := peer.fsm.pConf + if !original.AsPathOptions.Config.Equal(&c.AsPathOptions.Config) { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": peer.ID(), + }).Info("Update aspath options") + peer.fsm.pConf.AsPathOptions = c.AsPathOptions + needsSoftResetIn = true + } + if !original.Config.Equal(&c.Config) || !original.Transport.Config.Equal(&c.Transport.Config) || config.CheckAfiSafisChange(original.AfiSafis, c.AfiSafis) { sub := uint8(bgp.BGP_ERROR_SUB_OTHER_CONFIGURATION_CHANGE) if original.Config.AdminDown != c.Config.AdminDown { @@ -1738,7 +1757,7 @@ func (s *BgpServer) UpdateNeighbor(c *config.Neighbor) (policyUpdated bool, err "Topic": "Peer", "Key": peer.ID(), "State": state, - }).Info("update admin-state configuration") + }).Info("Update admin-state configuration") } else if original.Config.PeerAs != c.Config.PeerAs { sub = bgp.BGP_ERROR_SUB_PEER_DECONFIGURED } @@ -1763,7 +1782,7 @@ func (s *BgpServer) UpdateNeighbor(c *config.Neighbor) (policyUpdated bool, err log.WithFields(log.Fields{ "Topic": "Peer", "Key": peer.ID(), - }).Info("update timer configuration") + }).Info("Update timer configuration") peer.fsm.pConf.Timers.Config = c.Timers.Config } @@ -1778,7 +1797,7 @@ func (s *BgpServer) UpdateNeighbor(c *config.Neighbor) (policyUpdated bool, err } return err }, true) - return policyUpdated, err + return needsSoftResetIn, err } func (s *BgpServer) addrToPeers(addr string) (l []*Peer, err error) { |