diff options
Diffstat (limited to 'server/peer.go')
-rw-r--r-- | server/peer.go | 1080 |
1 files changed, 174 insertions, 906 deletions
diff --git a/server/peer.go b/server/peer.go index 63c907ee..f3b6caf0 100644 --- a/server/peer.go +++ b/server/peer.go @@ -17,198 +17,77 @@ package server import ( "encoding/json" - "fmt" log "github.com/Sirupsen/logrus" "github.com/osrg/gobgp/api" "github.com/osrg/gobgp/config" "github.com/osrg/gobgp/packet" "github.com/osrg/gobgp/policy" "github.com/osrg/gobgp/table" - "gopkg.in/tomb.v2" "net" - "strconv" "time" ) const ( - FSM_CHANNEL_LENGTH = 1024 - FLOP_THRESHOLD = time.Second * 30 - MIN_CONNECT_RETRY = 10 + FLOP_THRESHOLD = time.Second * 30 + MIN_CONNECT_RETRY = 10 ) -type peerMsgType int - -const ( - _ peerMsgType = iota - PEER_MSG_PATH - PEER_MSG_PEER_DOWN -) - -type peerMsg struct { - msgType peerMsgType - msgData interface{} -} - type Peer struct { - t tomb.Tomb globalConfig config.Global - peerConfig config.Neighbor - connCh chan net.Conn - serverMsgCh chan *serverMsg - peerMsgCh chan *peerMsg - getActiveCh chan struct{} + config config.Neighbor fsm *FSM + rfMap map[bgp.RouteFamily]bool + capMap map[bgp.BGPCapabilityCode]bgp.ParameterCapabilityInterface adjRib *table.AdjRib - // peer and rib are always not one-to-one so should not be - // here but it's the simplest and works our first target. - rib *table.TableManager - isGlobalRib bool - rfMap map[bgp.RouteFamily]bool - capMap map[bgp.BGPCapabilityCode]bgp.ParameterCapabilityInterface - peerInfo *table.PeerInfo - siblings map[string]*serverMsgDataPeer - outgoing chan *bgp.BGPMessage - importPolicies []*policy.Policy - defaultImportPolicy config.DefaultPolicyType - exportPolicies []*policy.Policy - defaultExportPolicy config.DefaultPolicyType + peerInfo *table.PeerInfo + outgoing chan *bgp.BGPMessage } -func NewPeer(g config.Global, peer config.Neighbor, serverMsgCh chan *serverMsg, peerMsgCh chan *peerMsg, peerList []*serverMsgDataPeer, isGlobalRib bool, policyMap map[string]*policy.Policy) *Peer { - p := &Peer{ +func NewPeer(g config.Global, config config.Neighbor) *Peer { + peer := &Peer{ globalConfig: g, - peerConfig: peer, - connCh: make(chan net.Conn), - serverMsgCh: serverMsgCh, - peerMsgCh: peerMsgCh, - getActiveCh: make(chan struct{}), + config: config, rfMap: make(map[bgp.RouteFamily]bool), capMap: make(map[bgp.BGPCapabilityCode]bgp.ParameterCapabilityInterface), - isGlobalRib: isGlobalRib, - } - p.siblings = make(map[string]*serverMsgDataPeer) - for _, s := range peerList { - p.siblings[s.address.String()] = s } - p.fsm = NewFSM(&g, &peer, p.connCh) - peer.BgpNeighborCommonState.State = uint32(bgp.BGP_FSM_IDLE) - peer.BgpNeighborCommonState.Downtime = time.Now().Unix() - for _, rf := range peer.AfiSafiList { + + config.BgpNeighborCommonState.State = uint32(bgp.BGP_FSM_IDLE) + config.BgpNeighborCommonState.Downtime = time.Now().Unix() + for _, rf := range config.AfiSafiList { k, _ := bgp.GetRouteFamily(rf.AfiSafiName) - p.rfMap[k] = true + peer.rfMap[k] = true } - p.peerInfo = &table.PeerInfo{ - AS: peer.PeerAs, + peer.peerInfo = &table.PeerInfo{ + AS: config.PeerAs, LocalID: g.RouterId, - Address: peer.NeighborAddress, - } - if isGlobalRib { - p.peerInfo.ID = g.RouterId - } - rfList := p.configuredRFlist() - p.adjRib = table.NewAdjRib(rfList) - p.rib = table.NewTableManager(p.peerConfig.NeighborAddress.String(), rfList) - p.setPolicy(policyMap) - p.t.Go(p.loop) - if !peer.TransportOptions.PassiveMode && !isGlobalRib { - p.t.Go(p.connectLoop) + Address: config.NeighborAddress, } - return p + peer.adjRib = table.NewAdjRib(peer.configuredRFlist()) + peer.fsm = NewFSM(&g, &config) + return peer } -func (peer *Peer) setPolicy(policyMap map[string]*policy.Policy) { - // configure import policy - policyConfig := peer.peerConfig.ApplyPolicy - inPolicies := make([]*policy.Policy, 0) - for _, policyName := range policyConfig.ImportPolicies { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.peerConfig.NeighborAddress, - "PolicyName": policyName, - }).Info("import policy installed") - if pol, ok := policyMap[policyName]; ok { - log.Debug("import policy : ", pol) - inPolicies = append(inPolicies, pol) - } - } - peer.importPolicies = inPolicies - peer.defaultImportPolicy = policyConfig.DefaultImportPolicy - - // configure export policy - outPolicies := make([]*policy.Policy, 0) - for _, policyName := range policyConfig.ExportPolicies { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.peerConfig.NeighborAddress, - "PolicyName": policyName, - }).Info("export policy installed") - if pol, ok := policyMap[policyName]; ok { - log.Debug("export policy : ", pol) - outPolicies = append(outPolicies, pol) - } - } - peer.exportPolicies = outPolicies - peer.defaultExportPolicy = policyConfig.DefaultExportPolicy +func (peer *Peer) isRouteServerClient() bool { + return peer.config.RouteServer.RouteServerClient } func (peer *Peer) configuredRFlist() []bgp.RouteFamily { rfList := []bgp.RouteFamily{} - for _, rf := range peer.peerConfig.AfiSafiList { + for _, rf := range peer.config.AfiSafiList { k, _ := bgp.GetRouteFamily(rf.AfiSafiName) rfList = append(rfList, k) } return rfList } -func (peer *Peer) sendPathsToSiblings(pathList []table.Path) { - if len(pathList) == 0 { - return - } - for _, s := range peer.siblings { - var pm *peerMsg - if peer.peerConfig.RouteServer.RouteServerClient { - checkas := func(asnum uint32, p []table.Path) []table.Path { - plist := []table.Path{} - for _, path := range p { - asList := path.GetAsList() - send := true - for _, as := range asList { - if as == asnum { - send = false - break - } - } - if send { - plist = append(plist, path) - } - } - return plist - } - p := checkas(s.As, pathList) - if len(p) == 0 { - continue - } else { - pm = &peerMsg{ - msgType: PEER_MSG_PATH, - msgData: p, - } - } - } else { - pm = &peerMsg{ - msgType: PEER_MSG_PATH, - msgData: pathList, - } - } - s.peerMsgCh <- pm - } -} - -func (peer *Peer) handleBGPmessage(m *bgp.BGPMessage) { +func (peer *Peer) handleBGPmessage(m *bgp.BGPMessage) ([]table.Path, bool) { + pathList := []table.Path{} log.WithFields(log.Fields{ "Topic": "Peer", - "Key": peer.peerConfig.NeighborAddress, + "Key": peer.config.NeighborAddress, "data": m, }).Debug("received") + update := false switch m.Header.Type { case bgp.BGP_MSG_OPEN: @@ -245,7 +124,7 @@ func (peer *Peer) handleBGPmessage(m *bgp.BGPMessage) { // by using the smaller of its configured Hold Time and the Hold Time // received in the OPEN message. holdTime := float64(body.HoldTime) - myHoldTime := peer.fsm.peerConfig.Timers.HoldTime + myHoldTime := peer.config.Timers.HoldTime if holdTime > myHoldTime { peer.fsm.negotiatedHoldTime = myHoldTime } else { @@ -258,791 +137,60 @@ func (peer *Peer) handleBGPmessage(m *bgp.BGPMessage) { if _, ok := peer.rfMap[rf]; !ok { log.WithFields(log.Fields{ "Topic": "Peer", - "Key": peer.peerConfig.NeighborAddress, + "Key": peer.config.NeighborAddress, "Data": rf, }).Warn("Route family isn't supported") - return + break } if _, ok := peer.capMap[bgp.BGP_CAP_ROUTE_REFRESH]; ok { - pathList := peer.adjRib.GetOutPathList(rf) - peer.sendMessages(table.CreateUpdateMsgFromPaths(pathList)) + pathList = peer.adjRib.GetOutPathList(rf) } else { log.WithFields(log.Fields{ "Topic": "Peer", - "Key": peer.peerConfig.NeighborAddress, + "Key": peer.config.NeighborAddress, }).Warn("ROUTE_REFRESH received but the capability wasn't advertised") } + case bgp.BGP_MSG_UPDATE: - peer.peerConfig.BgpNeighborCommonState.UpdateRecvTime = time.Now().Unix() + update = true + peer.config.BgpNeighborCommonState.UpdateRecvTime = time.Now().Unix() body := m.Body.(*bgp.BGPUpdate) _, err := bgp.ValidateUpdateMsg(body, peer.rfMap) if err != nil { log.WithFields(log.Fields{ "Topic": "Peer", - "Key": peer.peerConfig.NeighborAddress, + "Key": peer.config.NeighborAddress, "error": err, }).Warn("malformed BGP update message") m := err.(*bgp.MessageError) if m.TypeCode != 0 { peer.outgoing <- bgp.NewBGPNotificationMessage(m.TypeCode, m.SubTypeCode, m.Data) } - return + break } table.UpdatePathAttrs4ByteAs(body) - pathList := table.ProcessMessage(m, peer.peerInfo) + pathList = table.ProcessMessage(m, peer.peerInfo) peer.adjRib.UpdateIn(pathList) - peer.sendPathsToSiblings(pathList) - } -} - -func (peer *Peer) sendMessages(msgs []*bgp.BGPMessage) { - for _, m := range msgs { - if peer.peerConfig.BgpNeighborCommonState.State != uint32(bgp.BGP_FSM_ESTABLISHED) { - continue - } - - if m.Header.Type != bgp.BGP_MSG_UPDATE { - log.Fatal("not update message ", m.Header.Type) - } - - _, y := peer.capMap[bgp.BGP_CAP_FOUR_OCTET_AS_NUMBER] - if !y { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.peerConfig.NeighborAddress, - "data": m, - }).Debug("update for 2byte AS peer") - table.UpdatePathAttrs2ByteAs(m.Body.(*bgp.BGPUpdate)) - } - - peer.outgoing <- m - } -} - -func (peer *Peer) handleGrpc(grpcReq *GrpcRequest) { - result := &GrpcResponse{} - switch grpcReq.RequestType { - case REQ_GLOBAL_ADD, REQ_GLOBAL_DELETE: - rf := grpcReq.RouteFamily - path, ok := grpcReq.Data.(*api.Path) - if !ok { - result.ResponseErr = fmt.Errorf("type assertion failed") - grpcReq.ResponseCh <- result - close(grpcReq.ResponseCh) - return - } - var isWithdraw bool - if grpcReq.RequestType == REQ_GLOBAL_DELETE { - isWithdraw = true - } - - var nlri bgp.AddrPrefixInterface - pattr := make([]bgp.PathAttributeInterface, 0) - pattr = append(pattr, bgp.NewPathAttributeOrigin(bgp.BGP_ORIGIN_ATTR_TYPE_IGP)) - asparam := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{peer.peerInfo.AS}) - pattr = append(pattr, bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{asparam})) - - switch rf { - case bgp.RF_IPv4_UC: - ip, net, _ := net.ParseCIDR(path.Nlri.Prefix) - if ip.To4() == nil { - result.ResponseErr = fmt.Errorf("Invalid ipv4 prefix: %s", path.Nlri.Prefix) - grpcReq.ResponseCh <- result - close(grpcReq.ResponseCh) - return - } - ones, _ := net.Mask.Size() - nlri = &bgp.NLRInfo{ - IPAddrPrefix: *bgp.NewIPAddrPrefix(uint8(ones), ip.String()), - } - - pattr = append(pattr, bgp.NewPathAttributeNextHop("0.0.0.0")) - - case bgp.RF_IPv6_UC: - - ip, net, _ := net.ParseCIDR(path.Nlri.Prefix) - if ip.To16() == nil { - result.ResponseErr = fmt.Errorf("Invalid ipv6 prefix: %s", path.Nlri.Prefix) - grpcReq.ResponseCh <- result - close(grpcReq.ResponseCh) - return - } - ones, _ := net.Mask.Size() - nlri = bgp.NewIPv6AddrPrefix(uint8(ones), ip.String()) - - pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI("::", []bgp.AddrPrefixInterface{nlri})) - - case bgp.RF_EVPN: - mac, err := net.ParseMAC(path.Nlri.EvpnNlri.MacIpAdv.MacAddr) - if err != nil { - result.ResponseErr = fmt.Errorf("Invalid mac: %s", path.Nlri.EvpnNlri.MacIpAdv.MacAddr) - grpcReq.ResponseCh <- result - close(grpcReq.ResponseCh) - return - } - ip := net.ParseIP(path.Nlri.EvpnNlri.MacIpAdv.IpAddr) - if ip == nil { - result.ResponseErr = fmt.Errorf("Invalid ip prefix: %s", path.Nlri.EvpnNlri.MacIpAdv.IpAddr) - grpcReq.ResponseCh <- result - close(grpcReq.ResponseCh) - return - } - iplen := net.IPv4len * 8 - if ip.To4() == nil { - iplen = net.IPv6len * 8 - } - - macIpAdv := &bgp.EVPNMacIPAdvertisementRoute{ - RD: bgp.NewRouteDistinguisherTwoOctetAS(0, 0), - ESI: bgp.EthernetSegmentIdentifier{ - Type: bgp.ESI_ARBITRARY, - }, - MacAddressLength: 48, - MacAddress: mac, - IPAddressLength: uint8(iplen), - IPAddress: ip, - Labels: []uint32{0}, - } - nlri = bgp.NewEVPNNLRI(bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT, 0, macIpAdv) - pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI("0.0.0.0", []bgp.AddrPrefixInterface{nlri})) - case bgp.RF_ENCAP: - endpoint := net.ParseIP(path.Nlri.Prefix) - if endpoint == nil { - result.ResponseErr = fmt.Errorf("Invalid endpoint ip address: %s", path.Nlri.Prefix) - grpcReq.ResponseCh <- result - close(grpcReq.ResponseCh) - return - - } - nlri = bgp.NewEncapNLRI(endpoint.String()) - pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI("0.0.0.0", []bgp.AddrPrefixInterface{nlri})) - - iterSubTlvs := func(subTlvs []*api.TunnelEncapSubTLV) { - for _, subTlv := range subTlvs { - if subTlv.Type == api.ENCAP_SUBTLV_TYPE_COLOR { - color := subTlv.Color - subTlv := &bgp.TunnelEncapSubTLV{ - Type: bgp.ENCAP_SUBTLV_TYPE_COLOR, - Value: &bgp.TunnelEncapSubTLVColor{color}, - } - tlv := &bgp.TunnelEncapTLV{ - Type: bgp.TUNNEL_TYPE_VXLAN, - Value: []*bgp.TunnelEncapSubTLV{subTlv}, - } - attr := bgp.NewPathAttributeTunnelEncap([]*bgp.TunnelEncapTLV{tlv}) - pattr = append(pattr, attr) - break - } - } - } - - iterTlvs := func(tlvs []*api.TunnelEncapTLV) { - for _, tlv := range tlvs { - if tlv.Type == api.TUNNEL_TYPE_VXLAN { - iterSubTlvs(tlv.SubTlv) - break - } - } - } - - func(attrs []*api.PathAttr) { - for _, attr := range attrs { - if attr.Type == api.BGP_ATTR_TYPE_TUNNEL_ENCAP { - iterTlvs(attr.TunnelEncap) - break - } - } - }(path.Attrs) - - case bgp.RF_RTC_UC: - var ec bgp.ExtendedCommunityInterface - target := path.Nlri.RtNlri.Target - ec_type := target.Type - ec_subtype := target.Subtype - switch ec_type { - case api.EXTENDED_COMMUNITIE_TYPE_TWO_OCTET_AS_SPECIFIC: - if target.Asn == 0 && target.LocalAdmin == 0 { - break - } - ec = &bgp.TwoOctetAsSpecificExtended{ - SubType: bgp.ExtendedCommunityAttrSubType(ec_subtype), - AS: uint16(target.Asn), - LocalAdmin: target.LocalAdmin, - IsTransitive: true, - } - default: - result.ResponseErr = fmt.Errorf("Invalid endpoint ip address: %s", path.Nlri.Prefix) - grpcReq.ResponseCh <- result - close(grpcReq.ResponseCh) - return - } - - nlri = bgp.NewRouteTargetMembershipNLRI(peer.globalConfig.As, ec) - - pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI("0.0.0.0", []bgp.AddrPrefixInterface{nlri})) - - default: - result.ResponseErr = fmt.Errorf("Unsupported address family: %s", rf) - grpcReq.ResponseCh <- result - close(grpcReq.ResponseCh) - return - } - - p, err := table.CreatePath(peer.peerInfo, nlri, pattr, isWithdraw, time.Now()) - if err != nil { - result.ResponseErr = err - grpcReq.ResponseCh <- result - close(grpcReq.ResponseCh) - return - } - - pm := &peerMsg{ - msgType: PEER_MSG_PATH, - msgData: []table.Path{p}, - } - peer.peerMsgCh <- pm - - case REQ_LOCAL_RIB, REQ_GLOBAL_RIB: - if peer.fsm.adminState == ADMIN_STATE_DOWN { - close(grpcReq.ResponseCh) - return - } - if t, ok := peer.rib.Tables[grpcReq.RouteFamily]; ok { - for _, dst := range t.GetDestinations() { - result := &GrpcResponse{} - result.Data = dst.ToApiStruct() - grpcReq.ResponseCh <- result - } - } - close(grpcReq.ResponseCh) - return - case REQ_NEIGHBOR_SHUTDOWN: - peer.outgoing <- bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN, nil) - case REQ_NEIGHBOR_RESET: - peer.fsm.idleHoldTime = peer.peerConfig.Timers.IdleHoldTimeAfterReset - peer.outgoing <- bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_ADMINISTRATIVE_RESET, nil) - case REQ_NEIGHBOR_SOFT_RESET, REQ_NEIGHBOR_SOFT_RESET_IN: - // soft-reconfiguration inbound - peer.sendPathsToSiblings(peer.adjRib.GetInPathList(grpcReq.RouteFamily)) - if grpcReq.RequestType == REQ_NEIGHBOR_SOFT_RESET_IN { - break - } - fallthrough - case REQ_NEIGHBOR_SOFT_RESET_OUT: - pathList := peer.adjRib.GetOutPathList(grpcReq.RouteFamily) - peer.sendMessages(table.CreateUpdateMsgFromPaths(pathList)) - case REQ_ADJ_RIB_IN, REQ_ADJ_RIB_OUT: - rf := grpcReq.RouteFamily - var paths []table.Path - - if grpcReq.RequestType == REQ_ADJ_RIB_IN { - paths = peer.adjRib.GetInPathList(rf) - log.Debugf("RouteFamily=%v adj-rib-in found : %d", rf.String(), len(paths)) - } else { - paths = peer.adjRib.GetOutPathList(rf) - log.Debugf("RouteFamily=%v adj-rib-out found : %d", rf.String(), len(paths)) - } - - for _, p := range paths { - result := &GrpcResponse{} - path := &api.Path{} - j, _ := json.Marshal(p) - err := json.Unmarshal(j, path) - if err != nil { - result.ResponseErr = err - } else { - result.Data = path - } - grpcReq.ResponseCh <- result - } - close(grpcReq.ResponseCh) - return - case REQ_NEIGHBOR_ENABLE, REQ_NEIGHBOR_DISABLE: - var err api.Error - if grpcReq.RequestType == REQ_NEIGHBOR_ENABLE { - select { - case peer.fsm.adminStateCh <- ADMIN_STATE_UP: - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.peerConfig.NeighborAddress, - }).Debug("ADMIN_STATE_UP requested") - err.Code = api.Error_SUCCESS - err.Msg = "ADMIN_STATE_UP" - default: - log.Warning("previous request is still remaining. : ", peer.peerConfig.NeighborAddress) - err.Code = api.Error_FAIL - err.Msg = "previous request is still remaining" - } - } else { - select { - case peer.fsm.adminStateCh <- ADMIN_STATE_DOWN: - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.peerConfig.NeighborAddress, - }).Debug("ADMIN_STATE_DOWN requested") - err.Code = api.Error_SUCCESS - err.Msg = "ADMIN_STATE_DOWN" - default: - log.Warning("previous request is still remaining. : ", peer.peerConfig.NeighborAddress) - err.Code = api.Error_FAIL - err.Msg = "previous request is still remaining" - } - } - result.Data = err - case REQ_NEIGHBOR_POLICY: - resInPolicies := []*api.PolicyDefinition{} - resOutPolicies := []*api.PolicyDefinition{} - // Add importpolies that has been set in the configuration file to the list. - // However, peer haven't target importpolicy when add PolicyDefinition of name only to the list. - conInPolicyNames := peer.peerConfig.ApplyPolicy.ImportPolicies - for _, conInPolicyName := range conInPolicyNames { - match := false - for _, inPolicy := range peer.importPolicies { - if conInPolicyName == inPolicy.Name { - match = true - resInPolicies = append(resInPolicies, inPolicy.ToApiStruct()) - break - } - } - if !match { - resInPolicies = append(resInPolicies, &api.PolicyDefinition{PolicyDefinitionName: conInPolicyName}) - } - } - // Add importpolies that has been set in the configuration file to the list. - // However, peer haven't target importpolicy when add PolicyDefinition of name only to the list. - conOutPolicyNames := peer.peerConfig.ApplyPolicy.ExportPolicies - for _, conOutPolicyName := range conOutPolicyNames { - match := false - for _, outPolicy := range peer.exportPolicies { - if conOutPolicyName == outPolicy.Name { - match = true - resOutPolicies = append(resOutPolicies, outPolicy.ToApiStruct()) - break - } - } - if !match { - resOutPolicies = append(resOutPolicies, &api.PolicyDefinition{PolicyDefinitionName: conOutPolicyName}) - } - } - defaultInPolicy := policy.ROUTE_REJECT - defaultOutPolicy := policy.ROUTE_REJECT - if peer.defaultImportPolicy == config.DEFAULT_POLICY_TYPE_ACCEPT_ROUTE { - defaultInPolicy = policy.ROUTE_ACCEPT - } - if peer.defaultExportPolicy == config.DEFAULT_POLICY_TYPE_ACCEPT_ROUTE { - defaultOutPolicy = policy.ROUTE_ACCEPT - } - result.Data = &api.ApplyPolicy{ - DefaultImportPolicy: defaultInPolicy, - ImportPolicies: resInPolicies, - DefaultExportPolicy: defaultOutPolicy, - ExportPolicies: resOutPolicies, - } - case REQ_NEIGHBOR_POLICY_ADD_IMPORT, REQ_NEIGHBOR_POLICY_ADD_EXPORT, REQ_NEIGHBOR_POLICY_DEL_IMPORT, REQ_NEIGHBOR_POLICY_DEL_EXPORT: - data := grpcReq.Data.([]interface{}) - reqApplyPolicy := data[0].(*api.ApplyPolicy) - reqPolicyMap := data[1].(map[string]*policy.Policy) - applyPolicy := &peer.peerConfig.ApplyPolicy - var defInPolicy, defOutPolicy config.DefaultPolicyType - if grpcReq.RequestType == REQ_NEIGHBOR_POLICY_ADD_IMPORT { - if reqApplyPolicy.DefaultImportPolicy != policy.ROUTE_ACCEPT { - defInPolicy = config.DEFAULT_POLICY_TYPE_REJECT_ROUTE - } - peer.peerConfig.ApplyPolicy.DefaultImportPolicy = defInPolicy - applyPolicy.ImportPolicies = policy.PoliciesToString(reqApplyPolicy.ImportPolicies) - } else if grpcReq.RequestType == REQ_NEIGHBOR_POLICY_ADD_EXPORT { - if reqApplyPolicy.DefaultExportPolicy != policy.ROUTE_ACCEPT { - defOutPolicy = config.DEFAULT_POLICY_TYPE_REJECT_ROUTE - } - peer.peerConfig.ApplyPolicy.DefaultExportPolicy = defOutPolicy - applyPolicy.ExportPolicies = policy.PoliciesToString(reqApplyPolicy.ExportPolicies) - } else if grpcReq.RequestType == REQ_NEIGHBOR_POLICY_DEL_IMPORT { - peer.peerConfig.ApplyPolicy.DefaultImportPolicy = config.DEFAULT_POLICY_TYPE_ACCEPT_ROUTE - peer.peerConfig.ApplyPolicy.ImportPolicies = make([]string, 0) - } else if grpcReq.RequestType == REQ_NEIGHBOR_POLICY_DEL_EXPORT { - peer.peerConfig.ApplyPolicy.DefaultExportPolicy = config.DEFAULT_POLICY_TYPE_ACCEPT_ROUTE - peer.peerConfig.ApplyPolicy.ExportPolicies = make([]string, 0) - } - peer.setPolicy(reqPolicyMap) - } - grpcReq.ResponseCh <- result - close(grpcReq.ResponseCh) -} - -func (peer *Peer) sendUpdateMsgFromPaths(pList []table.Path) { - pList = func(arg []table.Path) []table.Path { - ret := make([]table.Path, 0, len(arg)) - for _, path := range arg { - if _, ok := peer.rfMap[path.GetRouteFamily()]; !ok { - continue - } - if peer.peerConfig.NeighborAddress.Equal(path.GetSource().Address) { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.peerConfig.NeighborAddress, - "Data": path, - }).Debug("From me, ignore.") - continue - } - - if peer.peerConfig.PeerAs == path.GetSourceAs() { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.peerConfig.NeighborAddress, - "Data": path, - }).Debug("AS PATH loop, ignore.") - continue - } - - if !path.IsWithdraw() { - var applied bool = false - applied, path = peer.applyPolicies(peer.exportPolicies, path) - if applied { - if path == nil { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.peerConfig.NeighborAddress, - "Data": path, - }).Debug("Export policy applied and rejected.") - continue - } - } else if peer.defaultExportPolicy != config.DEFAULT_POLICY_TYPE_ACCEPT_ROUTE { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.peerConfig.NeighborAddress, - "Data": path, - }).Debug("Default export policy applied and rejected.") - continue - } - } - - ret = append(ret, path.Clone(path.IsWithdraw())) - } - return ret - }(pList) - - peer.adjRib.UpdateOut(pList) - - if bgp.FSMState(peer.peerConfig.BgpNeighborCommonState.State) != bgp.BGP_FSM_ESTABLISHED || len(pList) == 0 { - return - } - - pList = func(arg []table.Path) []table.Path { - ret := make([]table.Path, 0, len(arg)) - for _, path := range pList { - isLocal := path.GetSource().ID.Equal(peer.peerInfo.LocalID) - if isLocal { - path.SetNexthop(peer.peerConfig.LocalAddress) - } else { - table.UpdatePathAttrs(path, &peer.globalConfig, &peer.peerConfig) - } - - ret = append(ret, path) - } - return ret - }(pList) - - peer.sendMessages(table.CreateUpdateMsgFromPaths(pList)) -} - -// apply policies to the path -// if multiple policies are defined, -// this function applies each policy to the path in the order that -// policies are stored in the array passed to this function. -// -// the way of applying statements inside a single policy -// - apply statement until the condition in the statement matches. -// if the condition matches the path, apply the action on the statement and -// return value that indicates 'applied' to caller of this function -// - if no statement applied, then process the next policy -// -// if no policy applied, return value that indicates 'not applied' to the caller of this function -// -// return values: -// bool -- indicates that any of policy applied to the path that is passed to this function -// table.Path -- indicates new path object that is the result of modification according to -// policy's action. -// If the applied policy doesn't have a modification action, -// then return the path itself that is passed to this function, otherwise return -// modified path. -// If action of the policy is 'reject', return nil -// -func (peer *Peer) applyPolicies(policies []*policy.Policy, original table.Path) (bool, table.Path) { - - var applied bool = true - - for _, pol := range policies { - if result, action, newpath := pol.Apply(original); result { - log.Debug("newpath: ", newpath) - if action == policy.ROUTE_TYPE_REJECT { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.peerConfig.NeighborAddress, - "NRLI": original.GetNlri(), - }).Debug("path was rejected") - // return applied, nil, this means path was rejected - return applied, nil - } else { - // return applied, new path - return applied, newpath - } - } } - log.Debug("no policy applied.", original) - // return not applied, original path - return !applied, original + return pathList, update } -func (peer *Peer) handlePeerMsg(m *peerMsg) { - switch m.msgType { - case PEER_MSG_PATH: - pList := m.msgData.([]table.Path) - - tmp := make([]table.Path, 0, len(pList)) - for _, path := range pList { - if path.IsWithdraw() { - tmp = append(tmp, path) - continue - } - - applied, path := peer.applyPolicies(peer.importPolicies, path) - if applied && path == nil { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.peerConfig.NeighborAddress, - "Data": path, - }).Debug("Import policy applied, reject.") - continue - } else if peer.defaultImportPolicy != config.DEFAULT_POLICY_TYPE_ACCEPT_ROUTE { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.peerConfig.NeighborAddress, - "Data": path, - }).Debug("Default import policy applied, reject.") - continue - } - - tmp = append(tmp, path) - } - pList = tmp - - if peer.peerConfig.RouteServer.RouteServerClient || peer.isGlobalRib { - pList, _ = peer.rib.ProcessPaths(pList) - } - - if peer.isGlobalRib { - peer.sendPathsToSiblings(pList) - } else { - peer.sendUpdateMsgFromPaths(pList) - } - - case PEER_MSG_PEER_DOWN: - for _, rf := range peer.configuredRFlist() { - pList, _ := peer.rib.DeletePathsforPeer(m.msgData.(*table.PeerInfo), rf) - if peer.peerConfig.RouteServer.RouteServerClient { - peer.sendUpdateMsgFromPaths(pList) - } else if peer.isGlobalRib { - peer.sendPathsToSiblings(pList) - } - } - } -} - -func (peer *Peer) handleServerMsg(m *serverMsg) { - switch m.msgType { - case SRV_MSG_PEER_ADDED: - d := m.msgData.(*serverMsgDataPeer) - peer.siblings[d.address.String()] = d - for _, rf := range peer.configuredRFlist() { - if peer.peerConfig.RouteServer.RouteServerClient { - peer.sendPathsToSiblings(peer.adjRib.GetInPathList(rf)) - } else if peer.isGlobalRib { - peer.sendPathsToSiblings(peer.rib.GetPathList(rf)) - } - } - case SRV_MSG_PEER_DELETED: - d := m.msgData.(*table.PeerInfo) - if _, ok := peer.siblings[d.Address.String()]; ok { - delete(peer.siblings, d.Address.String()) - for _, rf := range peer.configuredRFlist() { - pList, _ := peer.rib.DeletePathsforPeer(d, rf) - if peer.peerConfig.RouteServer.RouteServerClient { - peer.sendUpdateMsgFromPaths(pList) - } else { - peer.sendPathsToSiblings(pList) - } - } - } else { - log.Warning("can not find peer: ", d.Address.String()) - } - case SRV_MSG_API: - peer.handleGrpc(m.msgData.(*GrpcRequest)) - case SRV_MSG_POLICY_UPDATED: - log.Debug("policy updated") - d := m.msgData.(map[string]*policy.Policy) - peer.setPolicy(d) - default: - log.Fatal("unknown server msg type ", m.msgType) - } -} - -func (peer *Peer) connectLoop() error { - var tick int - if tick = int(peer.fsm.peerConfig.Timers.ConnectRetry); tick < MIN_CONNECT_RETRY { - tick = MIN_CONNECT_RETRY - } - - ticker := time.NewTicker(time.Duration(tick) * time.Second) - ticker.Stop() - - connect := func() { - if bgp.FSMState(peer.peerConfig.BgpNeighborCommonState.State) == bgp.BGP_FSM_ACTIVE { - var host string - addr := peer.peerConfig.NeighborAddress - - if addr.To4() != nil { - host = addr.String() + ":" + strconv.Itoa(bgp.BGP_PORT) - } else { - host = "[" + addr.String() + "]:" + strconv.Itoa(bgp.BGP_PORT) - } - - conn, err := net.DialTimeout("tcp", host, time.Duration(MIN_CONNECT_RETRY-1)*time.Second) - if err == nil { - peer.connCh <- conn - } else { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.peerConfig.NeighborAddress, - }).Debugf("failed to connect: %s", err) - } - } - } - - for { - select { - case <-peer.t.Dying(): - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.peerConfig.NeighborAddress, - }).Debug("stop connect loop") - ticker.Stop() - return nil - case <-ticker.C: - connect() - case <-peer.getActiveCh: - ticker = time.NewTicker(time.Duration(tick) * time.Second) - } - } -} - -// this goroutine handles routing table operations -func (peer *Peer) loop() error { - for { - incoming := make(chan *fsmMsg, FSM_CHANNEL_LENGTH) - peer.outgoing = make(chan *bgp.BGPMessage, FSM_CHANNEL_LENGTH) - - var h *FSMHandler - - if !peer.isGlobalRib { - h = NewFSMHandler(peer.fsm, incoming, peer.outgoing) - switch peer.peerConfig.BgpNeighborCommonState.State { - case uint32(bgp.BGP_FSM_ESTABLISHED): - peer.peerConfig.LocalAddress = peer.fsm.LocalAddr() - for rf, _ := range peer.rfMap { - pathList := peer.adjRib.GetOutPathList(rf) - if !peer.peerConfig.RouteServer.RouteServerClient { - for _, path := range pathList { - path.SetNexthop(peer.peerConfig.LocalAddress) - } - } - peer.sendMessages(table.CreateUpdateMsgFromPaths(pathList)) - } - peer.fsm.peerConfig.BgpNeighborCommonState.Uptime = time.Now().Unix() - peer.fsm.peerConfig.BgpNeighborCommonState.EstablishedCount++ - case uint32(bgp.BGP_FSM_ACTIVE): - if !peer.peerConfig.TransportOptions.PassiveMode { - peer.getActiveCh <- struct{}{} - } - fallthrough - default: - peer.fsm.peerConfig.BgpNeighborCommonState.Downtime = time.Now().Unix() - } - } - - sameState := true - for sameState { - select { - case <-peer.t.Dying(): - close(peer.connCh) - peer.outgoing <- bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_PEER_DECONFIGURED, nil) - // h.t.Kill(nil) will be called - // internall so even goroutines in - // non-established will be killed. - h.Stop() - return nil - case e := <-incoming: - switch e.MsgType { - case FSM_MSG_STATE_CHANGE: - nextState := e.MsgData.(bgp.FSMState) - // waits for all goroutines created for the current state - h.Wait() - oldState := bgp.FSMState(peer.peerConfig.BgpNeighborCommonState.State) - peer.peerConfig.BgpNeighborCommonState.State = uint32(nextState) - peer.fsm.StateChange(nextState) - sameState = false - if oldState == bgp.BGP_FSM_ESTABLISHED { - t := time.Now() - if t.Sub(time.Unix(peer.fsm.peerConfig.BgpNeighborCommonState.Uptime, 0)) < FLOP_THRESHOLD { - peer.fsm.peerConfig.BgpNeighborCommonState.Flops++ - } - - for _, rf := range peer.configuredRFlist() { - peer.adjRib.DropAllIn(rf) - } - pm := &peerMsg{ - msgType: PEER_MSG_PEER_DOWN, - msgData: peer.peerInfo, - } - for _, s := range peer.siblings { - s.peerMsgCh <- pm - } - } - - // clear counter - if h.fsm.adminState == ADMIN_STATE_DOWN { - h.fsm.peerConfig.BgpNeighborCommonState = config.BgpNeighborCommonState{} - } - - case FSM_MSG_BGP_MESSAGE: - switch m := e.MsgData.(type) { - case *bgp.MessageError: - peer.outgoing <- bgp.NewBGPNotificationMessage(m.TypeCode, m.SubTypeCode, m.Data) - case *bgp.BGPMessage: - peer.handleBGPmessage(m) - default: - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.peerConfig.NeighborAddress, - "Data": e.MsgData, - }).Panic("unknonw msg type") - } - } - case m := <-peer.serverMsgCh: - peer.handleServerMsg(m) - case m := <-peer.peerMsgCh: - peer.handlePeerMsg(m) - } +func (peer *Peer) getBests(loc *LocalRib) []table.Path { + pathList := []table.Path{} + for _, rf := range peer.configuredRFlist() { + for _, dst := range loc.rib.Tables[rf].GetDestinations() { + pathList = append(pathList, dst.GetBestPath()) } } + return pathList } -func (peer *Peer) Stop() error { - peer.t.Kill(nil) - return peer.t.Wait() +func (peer *Peer) startFSMHandler(incoming chan *fsmMsg) { + peer.fsm.h = NewFSMHandler(peer.fsm, incoming, peer.outgoing) } func (peer *Peer) PassConn(conn *net.TCPConn) { - peer.connCh <- conn + peer.fsm.connCh <- conn } func (peer *Peer) MarshalJSON() ([]byte, error) { @@ -1059,7 +207,7 @@ func (peer *Peer) ToApiStruct() *api.Peer { remoteCap = append(remoteCap, c.ToApiStruct()) } - caps := capabilitiesFromConfig(&peer.globalConfig, &peer.peerConfig) + caps := capabilitiesFromConfig(&peer.globalConfig, &peer.config) localCap := make([]*api.Capability, 0, len(caps)) for _, c := range caps { localCap = append(localCap, c.ToApiStruct()) @@ -1071,8 +219,8 @@ func (peer *Peer) ToApiStruct() *api.Peer { RemoteAs: c.PeerAs, RemoteCap: remoteCap, LocalCap: localCap, - KeepaliveInterval: uint32(peer.fsm.peerConfig.Timers.KeepaliveInterval), - Holdtime: uint32(peer.fsm.peerConfig.Timers.HoldTime), + KeepaliveInterval: uint32(peer.config.Timers.KeepaliveInterval), + Holdtime: uint32(peer.config.Timers.HoldTime), } s := c.BgpNeighborCommonState @@ -1099,10 +247,10 @@ func (peer *Peer) ToApiStruct() *api.Peer { keepalive := uint32(0) if f.negotiatedHoldTime != 0 { - if f.negotiatedHoldTime < f.peerConfig.Timers.HoldTime { + if f.negotiatedHoldTime < c.Timers.HoldTime { keepalive = uint32(f.negotiatedHoldTime / 3) } else { - keepalive = uint32(f.peerConfig.Timers.KeepaliveInterval) + keepalive = uint32(c.Timers.KeepaliveInterval) } } @@ -1140,3 +288,123 @@ func (peer *Peer) ToApiStruct() *api.Peer { Info: info, } } + +type LocalRib struct { + rib *table.TableManager + importPolicies []*policy.Policy + defaultImportPolicy config.DefaultPolicyType + exportPolicies []*policy.Policy + defaultExportPolicy config.DefaultPolicyType +} + +func NewLocalRib(owner string, rfList []bgp.RouteFamily, policyMap map[string]*policy.Policy) *LocalRib { + return &LocalRib{ + rib: table.NewTableManager(owner, rfList), + } +} + +func (loc *LocalRib) OwnerName() string { + return loc.rib.OwnerName() +} + +func (loc *LocalRib) isGlobal() bool { + return loc.OwnerName() == "global" +} + +func (loc *LocalRib) setPolicy(peer *Peer, policyMap map[string]*policy.Policy) { + // configure import policy + policyConfig := peer.config.ApplyPolicy + inPolicies := make([]*policy.Policy, 0) + for _, policyName := range policyConfig.ImportPolicies { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": peer.config.NeighborAddress, + "PolicyName": policyName, + }).Info("import policy installed") + if pol, ok := policyMap[policyName]; ok { + log.Debug("import policy : ", pol) + inPolicies = append(inPolicies, pol) + } + } + loc.importPolicies = inPolicies + loc.defaultImportPolicy = policyConfig.DefaultImportPolicy + + // configure export policy + outPolicies := make([]*policy.Policy, 0) + for _, policyName := range policyConfig.ExportPolicies { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": peer.config.NeighborAddress, + "PolicyName": policyName, + }).Info("export policy installed") + if pol, ok := policyMap[policyName]; ok { + log.Debug("export policy : ", pol) + outPolicies = append(outPolicies, pol) + } + } + loc.exportPolicies = outPolicies + loc.defaultExportPolicy = policyConfig.DefaultExportPolicy +} + +// apply policies to the path +// if multiple policies are defined, +// this function applies each policy to the path in the order that +// policies are stored in the array passed to this function. +// +// the way of applying statements inside a single policy +// - apply statement until the condition in the statement matches. +// if the condition matches the path, apply the action on the statement and +// return value that indicates 'applied' to caller of this function +// - if no statement applied, then process the next policy +// +// if no policy applied, return value that indicates 'not applied' to the caller of this function +// +// return values: +// bool -- indicates that any of policy applied to the path that is passed to this function +// table.Path -- indicates new path object that is the result of modification according to +// policy's action. +// If the applied policy doesn't have a modification action, +// then return the path itself that is passed to this function, otherwise return +// modified path. +// If action of the policy is 'reject', return nil +// +func (loc *LocalRib) applyPolicies(isExport bool, original table.Path) (bool, table.Path) { + + var applied bool = true + var policies []*policy.Policy + var direction string + if isExport == true { + policies = loc.exportPolicies + direction = "export" + } else { + policies = loc.importPolicies + direction = "import" + } + + for _, pol := range policies { + if result, action, newpath := pol.Apply(original); result { + log.Debug("newpath: ", newpath) + if action == policy.ROUTE_TYPE_REJECT { + log.WithFields(log.Fields{ + "Topic": "Loc", + "Key": loc.OwnerName(), + "NRLI": original.GetNlri(), + "Dir": direction, + }).Debug("path was rejected") + // return applied, nil, this means path was rejected + return applied, nil + } else { + // return applied, new path + return applied, newpath + } + } + } + log.WithFields(log.Fields{ + "Topic": "Loc", + "Key": loc.OwnerName(), + "Len": len(policies), + "NRLI": original, + "Dir": direction, + }).Debug("no policy applied") + return !applied, original +} |