func NewPeer(g config.Global, conf config.Neighbor, loc *table.TableManager) *Peer { peer := &Peer{ gConf: g, conf: conf, rfMap: make(map[bgp.RouteFamily]bool), capMap: make(map[bgp.BGPCapabilityCode][]bgp.ParameterCapabilityInterface), outgoing: make(chan *bgp.BGPMessage, 128), localRib: loc, } conf.NeighborState.SessionState = uint32(bgp.BGP_FSM_IDLE) conf.Timers.TimersState.Downtime = time.Now().Unix() for _, rf := range conf.AfiSafis.AfiSafiList { k, _ := bgp.GetRouteFamily(rf.AfiSafiName) peer.rfMap[k] = true } id := net.ParseIP(string(conf.RouteReflector.RouteReflectorConfig.RouteReflectorClusterId)).To4() peer.peerInfo = &table.PeerInfo{ AS: conf.NeighborConfig.PeerAs, LocalAS: g.GlobalConfig.As, LocalID: g.GlobalConfig.RouterId, Address: conf.NeighborConfig.NeighborAddress, RouteReflectorClient: peer.isRouteReflectorClient(), RouteReflectorClusterID: id, } peer.adjRib = table.NewAdjRib(peer.configuredRFlist()) peer.fsm = NewFSM(&g, &conf) peer.isConfederationMember = config.IsConfederationMember(&g, &conf) return peer }
func NewFSM(gConf *config.Global, pConf *config.Neighbor, peer *Peer) *FSM { adminState := ADMIN_STATE_UP if pConf.NeighborState.AdminDown == true { adminState = ADMIN_STATE_DOWN } fsm := &FSM{ gConf: gConf, pConf: pConf, state: bgp.BGP_FSM_IDLE, connCh: make(chan net.Conn, 1), opensentHoldTime: float64(HOLDTIME_OPENSENT), adminState: adminState, adminStateCh: make(chan AdminState, 1), getActiveCh: make(chan struct{}), rfMap: make(map[bgp.RouteFamily]bool), confedCheck: !config.IsConfederationMember(gConf, pConf) && config.IsEBGPPeer(gConf, pConf), peerInfo: table.NewPeerInfo(gConf, pConf), peer: peer, } fsm.t.Go(fsm.connectLoop) return fsm }
func NewFSM(gConf *config.Global, pConf *config.Neighbor, policy *table.RoutingPolicy) *FSM { adminState := ADMIN_STATE_UP if pConf.State.AdminDown { adminState = ADMIN_STATE_DOWN } fsm := &FSM{ gConf: gConf, pConf: pConf, state: bgp.BGP_FSM_IDLE, connCh: make(chan net.Conn, 1), opensentHoldTime: float64(HOLDTIME_OPENSENT), adminState: adminState, adminStateCh: make(chan AdminState, 1), getActiveCh: make(chan struct{}), rfMap: make(map[bgp.RouteFamily]bool), capMap: make(map[bgp.BGPCapabilityCode][]bgp.ParameterCapabilityInterface), confedCheck: !config.IsConfederationMember(gConf, pConf) && config.IsEBGPPeer(gConf, pConf), peerInfo: table.NewPeerInfo(gConf, pConf), policy: policy, } fsm.t.Go(fsm.connectLoop) return fsm }
func (h *FSMHandler) recvMessageWithError() (*FsmMsg, error) { sendToErrorCh := func(reason FsmStateReason) { // probably doesn't happen but be cautious select { case h.errorCh <- reason: default: } } headerBuf, err := readAll(h.conn, bgp.BGP_HEADER_LENGTH) if err != nil { sendToErrorCh(FSM_READ_FAILED) return nil, err } hd := &bgp.BGPHeader{} err = hd.DecodeFromBytes(headerBuf) if err != nil { h.fsm.bgpMessageStateUpdate(0, true) log.WithFields(log.Fields{ "Topic": "Peer", "Key": h.fsm.pConf.Config.NeighborAddress, "State": h.fsm.state.String(), "error": err, }).Warn("malformed BGP Header") fmsg := &FsmMsg{ MsgType: FSM_MSG_BGP_MESSAGE, MsgSrc: h.fsm.pConf.Config.NeighborAddress, MsgData: err, Version: h.fsm.version, } return fmsg, err } bodyBuf, err := readAll(h.conn, int(hd.Len)-bgp.BGP_HEADER_LENGTH) if err != nil { sendToErrorCh(FSM_READ_FAILED) return nil, err } now := time.Now() m, err := bgp.ParseBGPBody(hd, bodyBuf) if err == nil { h.fsm.bgpMessageStateUpdate(m.Header.Type, true) err = bgp.ValidateBGPMessage(m) } else { h.fsm.bgpMessageStateUpdate(0, true) } fmsg := &FsmMsg{ MsgType: FSM_MSG_BGP_MESSAGE, MsgSrc: h.fsm.pConf.Config.NeighborAddress, timestamp: now, Version: h.fsm.version, } if err != nil { log.WithFields(log.Fields{ "Topic": "Peer", "Key": h.fsm.pConf.Config.NeighborAddress, "State": h.fsm.state.String(), "error": err, }).Warn("malformed BGP message") fmsg.MsgData = err } else { fmsg.MsgData = m if h.fsm.state == bgp.BGP_FSM_ESTABLISHED { switch m.Header.Type { case bgp.BGP_MSG_ROUTE_REFRESH: fmsg.MsgType = FSM_MSG_ROUTE_REFRESH 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) _, err := bgp.ValidateUpdateMsg(body, h.fsm.rfMap, confedCheck) if err != nil { log.WithFields(log.Fields{ "Topic": "Peer", "Key": h.fsm.pConf.Config.NeighborAddress, "State": h.fsm.state.String(), "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 } } 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 // isn't zero, the timer will be reset // soon anyway. select { case h.holdTimerResetCh <- true: default: } if m.Header.Type == bgp.BGP_MSG_KEEPALIVE { return nil, nil } case bgp.BGP_MSG_NOTIFICATION: body := m.Body.(*bgp.BGPNotification) log.WithFields(log.Fields{ "Topic": "Peer", "Key": h.fsm.pConf.Config.NeighborAddress, "Code": body.ErrorCode, "Subcode": body.ErrorSubcode, "Data": body.Data, }).Warn("received notification") sendToErrorCh(FsmStateReason(fmt.Sprintf("%s %s", FSM_NOTIFICATION_RECV, bgp.NewNotificationErrorCode(body.ErrorCode, body.ErrorSubcode).String()))) return nil, nil } } } return fmsg, err }
func (path *Path) UpdatePathAttrs(global *config.Global, peer *config.Neighbor) { if peer.RouteServer.RouteServerConfig.RouteServerClient { return } localAddress := peer.Transport.TransportConfig.LocalAddress if peer.NeighborConfig.PeerType == config.PEER_TYPE_EXTERNAL { // NEXTHOP handling path.SetNexthop(localAddress) // AS_PATH handling path.PrependAsn(global.GlobalConfig.As, 1) // MED Handling idx, _ := path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC) if idx >= 0 && !path.IsLocal() { path.pathAttrs = append(path.pathAttrs[:idx], path.pathAttrs[idx+1:]...) } // remove local-pref attribute idx, _ = path.getPathAttr(bgp.BGP_ATTR_TYPE_LOCAL_PREF) if idx >= 0 && !config.IsConfederationMember(global, peer) { path.pathAttrs = append(path.pathAttrs[:idx], path.pathAttrs[idx+1:]...) } } else if peer.NeighborConfig.PeerType == config.PEER_TYPE_INTERNAL { // NEXTHOP handling for iBGP // if the path generated locally set local address as nexthop. // if not, don't modify it. // TODO: NEXT-HOP-SELF support nexthop := path.GetNexthop() if path.IsLocal() && (nexthop.Equal(net.ParseIP("0.0.0.0")) || nexthop.Equal(net.ParseIP("::"))) { path.SetNexthop(localAddress) } // AS_PATH handling for iBGP // if the path has AS_PATH path attribute, don't modify it. // if not, attach *empty* AS_PATH path attribute. idx, _ := path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH) if idx < 0 { path.PrependAsn(0, 0) } // For iBGP peers we are required to send local-pref attribute // for connected or local prefixes. // We set default local-pref 100. p := bgp.NewPathAttributeLocalPref(100) idx, _ = path.getPathAttr(bgp.BGP_ATTR_TYPE_LOCAL_PREF) if idx < 0 { path.pathAttrs = append(path.pathAttrs, p) } else if !path.IsLocal() { path.pathAttrs[idx] = p } // RFC4456: BGP Route Reflection // 8. Avoiding Routing Information Loops info := path.source if peer.RouteReflector.RouteReflectorConfig.RouteReflectorClient { // This attribute will carry the BGP Identifier of the originator of the route in the local AS. // A BGP speaker SHOULD NOT create an ORIGINATOR_ID attribute if one already exists. idx, _ = path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGINATOR_ID) if idx < 0 { p := bgp.NewPathAttributeOriginatorId(info.ID.String()) path.pathAttrs = append(path.pathAttrs, p) } // When an RR reflects a route, it MUST prepend the local CLUSTER_ID to the CLUSTER_LIST. // If the CLUSTER_LIST is empty, it MUST create a new one. idx, _ = path.getPathAttr(bgp.BGP_ATTR_TYPE_CLUSTER_LIST) id := string(peer.RouteReflector.RouteReflectorConfig.RouteReflectorClusterId) if idx < 0 { p := bgp.NewPathAttributeClusterList([]string{id}) path.pathAttrs = append(path.pathAttrs, p) } else { p := path.pathAttrs[idx].(*bgp.PathAttributeClusterList) newClusterList := make([]string, 0, len(p.Value)) for _, ip := range p.Value { newClusterList = append(newClusterList, ip.String()) } path.pathAttrs[idx] = bgp.NewPathAttributeClusterList(append([]string{id}, newClusterList...)) } } } else { log.WithFields(log.Fields{ "Topic": "Peer", "Key": peer.NeighborConfig.NeighborAddress, }).Warnf("invalid peer type: %d", peer.NeighborConfig.PeerType) } }
func (h *FSMHandler) recvMessageWithError() error { headerBuf, err := readAll(h.conn, bgp.BGP_HEADER_LENGTH) if err != nil { h.errorCh <- FSM_READ_FAILED return err } hd := &bgp.BGPHeader{} err = hd.DecodeFromBytes(headerBuf) if err != nil { h.fsm.bgpMessageStateUpdate(0, true) log.WithFields(log.Fields{ "Topic": "Peer", "Key": h.fsm.pConf.Config.NeighborAddress, "State": h.fsm.state, "error": err, }).Warn("malformed BGP Header") h.msgCh <- &FsmMsg{ MsgType: FSM_MSG_BGP_MESSAGE, MsgSrc: h.fsm.pConf.Config.NeighborAddress, MsgDst: h.fsm.pConf.Transport.Config.LocalAddress, MsgData: err, } return err } bodyBuf, err := readAll(h.conn, int(hd.Len)-bgp.BGP_HEADER_LENGTH) if err != nil { h.errorCh <- FSM_READ_FAILED return err } now := time.Now() m, err := bgp.ParseBGPBody(hd, bodyBuf) if err == nil { h.fsm.bgpMessageStateUpdate(m.Header.Type, true) err = bgp.ValidateBGPMessage(m) } else { h.fsm.bgpMessageStateUpdate(0, true) } fmsg := &FsmMsg{ MsgType: FSM_MSG_BGP_MESSAGE, MsgSrc: h.fsm.pConf.Config.NeighborAddress, MsgDst: h.fsm.pConf.Transport.Config.LocalAddress, timestamp: now, } if err != nil { log.WithFields(log.Fields{ "Topic": "Peer", "Key": h.fsm.pConf.Config.NeighborAddress, "State": h.fsm.state, "error": err, }).Warn("malformed BGP message") fmsg.MsgData = err } else { fmsg.MsgData = m if h.fsm.state == bgp.BGP_FSM_ESTABLISHED { switch m.Header.Type { 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) _, err := bgp.ValidateUpdateMsg(body, h.fsm.rfMap, confedCheck) if err != nil { log.WithFields(log.Fields{ "Topic": "Peer", "Key": h.fsm.pConf.Config.NeighborAddress, "error": err, }).Warn("malformed BGP update message") fmsg.MsgData = err } else { // FIXME: we should use the original message for bmp/mrt table.UpdatePathAttrs4ByteAs(body) fmsg.PathList = table.ProcessMessage(m, h.fsm.peerInfo, fmsg.timestamp) id := h.fsm.pConf.Config.NeighborAddress policyMutex.RLock() for _, path := range fmsg.PathList { if h.fsm.policy.ApplyPolicy(id, table.POLICY_DIRECTION_IN, path, nil) == nil { path.Filter(id, table.POLICY_DIRECTION_IN) } } policyMutex.RUnlock() } 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 lenght of h.holdTimerResetCh // isn't zero, the timer will be reset // soon anyway. if len(h.holdTimerResetCh) == 0 { h.holdTimerResetCh <- true } if m.Header.Type == bgp.BGP_MSG_KEEPALIVE { return nil } case bgp.BGP_MSG_NOTIFICATION: body := m.Body.(*bgp.BGPNotification) log.WithFields(log.Fields{ "Topic": "Peer", "Key": h.fsm.pConf.Config.NeighborAddress, "Code": body.ErrorCode, "Subcode": body.ErrorSubcode, "Data": body.Data, }).Warn("received notification") h.errorCh <- FSM_NOTIFICATION_RECV return nil } } } h.msgCh <- fmsg return err }