func TestMrtBgp4mpMessage(t *testing.T) { msg := bgp.NewBGPKeepAliveMessage() m1 := NewBGP4MPMessage(65000, 65001, 1, "192.168.0.1", "192.168.0.2", false, msg) b1, err := m1.Serialize() if err != nil { t.Fatal(err) } m2 := &BGP4MPMessage{BGP4MPHeader: &BGP4MPHeader{}} err = m2.DecodeFromBytes(b1) if err != nil { t.Fatal(err) } assert.Equal(t, reflect.DeepEqual(m1, m2), true) }
func TestMrtSplit(t *testing.T) { var b bytes.Buffer numwrite, numread := 10, 0 for i := 0; i < numwrite; i++ { msg := bgp.NewBGPKeepAliveMessage() m1 := NewBGP4MPMessage(65000, 65001, 1, "192.168.0.1", "192.168.0.2", false, msg) mm, _ := NewMRTMessage(1234, BGP4MP, MESSAGE, m1) b1, err := mm.Serialize() if err != nil { t.Fatal(err) } b.Write(b1) } t.Logf("wrote %d serialized MRT keepalives in the buffer", numwrite) r := bytes.NewReader(b.Bytes()) scanner := bufio.NewScanner(r) scanner.Split(SplitMrt) for scanner.Scan() { numread += 1 } t.Logf("scanner scanned %d serialized keepalives from the buffer", numread) assert.Equal(t, numwrite, numread) }
func (h *FSMHandler) openconfirm() (bgp.FSMState, FsmStateReason) { fsm := h.fsm ticker := keepaliveTicker(fsm) h.msgCh = channels.NewInfiniteChannel() h.conn = fsm.conn h.t.Go(h.recvMessage) var holdTimer *time.Timer if fsm.pConf.Timers.State.NegotiatedHoldTime == 0 { holdTimer = &time.Timer{} } else { // RFC 4271 P.65 // sets the HoldTimer according to the negotiated value holdTimer = time.NewTimer(time.Second * time.Duration(fsm.pConf.Timers.State.NegotiatedHoldTime)) } for { select { case <-h.t.Dying(): h.conn.Close() return -1, FSM_DYING case conn, ok := <-fsm.connCh: if !ok { break } conn.Close() log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state.String(), }).Warn("Closed an accepted connection") case <-fsm.gracefulRestartTimer.C: if fsm.pConf.GracefulRestart.State.PeerRestarting { log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state.String(), }).Warn("graceful restart timer expired") return bgp.BGP_FSM_IDLE, FSM_RESTART_TIMER_EXPIRED } case <-ticker.C: m := bgp.NewBGPKeepAliveMessage() b, _ := m.Serialize() // TODO: check error fsm.conn.Write(b) fsm.bgpMessageStateUpdate(m.Header.Type, false) case i, ok := <-h.msgCh.Out(): if !ok { continue } e := i.(*FsmMsg) switch e.MsgData.(type) { case *bgp.BGPMessage: m := e.MsgData.(*bgp.BGPMessage) if m.Header.Type == bgp.BGP_MSG_KEEPALIVE { return bgp.BGP_FSM_ESTABLISHED, FSM_OPEN_MSG_NEGOTIATED } // send notification ? h.conn.Close() return bgp.BGP_FSM_IDLE, FSM_INVALID_MSG case *bgp.MessageError: fsm.sendNotificationFromErrorMsg(e.MsgData.(*bgp.MessageError)) return bgp.BGP_FSM_IDLE, FSM_INVALID_MSG default: log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state.String(), "Data": e.MsgData, }).Panic("unknown msg type") } case err := <-h.errorCh: h.conn.Close() return bgp.BGP_FSM_IDLE, err case <-holdTimer.C: fsm.sendNotification(bgp.BGP_ERROR_HOLD_TIMER_EXPIRED, 0, nil, "hold timer expired") h.t.Kill(nil) return bgp.BGP_FSM_IDLE, FSM_HOLD_TIMER_EXPIRED case s := <-fsm.adminStateCh: err := h.changeAdminState(s) if err == nil { switch s { case ADMIN_STATE_DOWN: h.conn.Close() return bgp.BGP_FSM_IDLE, FSM_ADMIN_DOWN case ADMIN_STATE_UP: log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state.String(), "AdminState": s.String(), }).Panic("code logic bug") } } } } }
func (h *FSMHandler) opensent() (bgp.FSMState, FsmStateReason) { fsm := h.fsm m := buildopen(fsm.gConf, fsm.pConf) b, _ := m.Serialize() fsm.conn.Write(b) fsm.bgpMessageStateUpdate(m.Header.Type, false) h.msgCh = channels.NewInfiniteChannel() h.conn = fsm.conn h.t.Go(h.recvMessage) // RFC 4271 P.60 // sets its HoldTimer to a large value // A HoldTimer value of 4 minutes is suggested as a "large value" // for the HoldTimer holdTimer := time.NewTimer(time.Second * time.Duration(fsm.opensentHoldTime)) for { select { case <-h.t.Dying(): h.conn.Close() return -1, FSM_DYING case conn, ok := <-fsm.connCh: if !ok { break } conn.Close() log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state.String(), }).Warn("Closed an accepted connection") case <-fsm.gracefulRestartTimer.C: if fsm.pConf.GracefulRestart.State.PeerRestarting { log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state.String(), }).Warn("graceful restart timer expired") return bgp.BGP_FSM_IDLE, FSM_RESTART_TIMER_EXPIRED } case i, ok := <-h.msgCh.Out(): if !ok { continue } e := i.(*FsmMsg) switch e.MsgData.(type) { case *bgp.BGPMessage: m := e.MsgData.(*bgp.BGPMessage) if m.Header.Type == bgp.BGP_MSG_OPEN { fsm.recvOpen = m body := m.Body.(*bgp.BGPOpen) err := bgp.ValidateOpenMsg(body, fsm.pConf.Config.PeerAs) if err != nil { fsm.sendNotificationFromErrorMsg(err.(*bgp.MessageError)) return bgp.BGP_FSM_IDLE, FSM_INVALID_MSG } fsm.peerInfo.ID = body.ID fsm.capMap, fsm.rfMap = open2Cap(body, fsm.pConf) // calculate HoldTime // RFC 4271 P.13 // a BGP speaker MUST calculate the value of the Hold Timer // by using the smaller of its configured Hold Time and the Hold Time // received in the OPEN message. holdTime := float64(body.HoldTime) myHoldTime := fsm.pConf.Timers.Config.HoldTime if holdTime > myHoldTime { fsm.pConf.Timers.State.NegotiatedHoldTime = myHoldTime } else { fsm.pConf.Timers.State.NegotiatedHoldTime = holdTime } keepalive := fsm.pConf.Timers.Config.KeepaliveInterval if n := fsm.pConf.Timers.State.NegotiatedHoldTime; n < myHoldTime { keepalive = n / 3 } fsm.pConf.Timers.State.KeepaliveInterval = keepalive gr, ok := fsm.capMap[bgp.BGP_CAP_GRACEFUL_RESTART] if fsm.pConf.GracefulRestart.Config.Enabled && ok { state := &fsm.pConf.GracefulRestart.State state.Enabled = true cap := gr[len(gr)-1].(*bgp.CapGracefulRestart) state.PeerRestartTime = uint16(cap.Time) for _, t := range cap.Tuples { n := bgp.AddressFamilyNameMap[bgp.AfiSafiToRouteFamily(t.AFI, t.SAFI)] for i, a := range fsm.pConf.AfiSafis { if string(a.Config.AfiSafiName) == n { fsm.pConf.AfiSafis[i].MpGracefulRestart.State.Enabled = true fsm.pConf.AfiSafis[i].MpGracefulRestart.State.Received = true break } } } // RFC 4724 4.1 // To re-establish the session with its peer, the Restarting Speaker // MUST set the "Restart State" bit in the Graceful Restart Capability // of the OPEN message. if fsm.pConf.GracefulRestart.State.PeerRestarting && cap.Flags != 0x08 { log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state.String(), }).Warn("restart flag is not set") // send notification? h.conn.Close() return bgp.BGP_FSM_IDLE, FSM_INVALID_MSG } } msg := bgp.NewBGPKeepAliveMessage() b, _ := msg.Serialize() fsm.conn.Write(b) fsm.bgpMessageStateUpdate(msg.Header.Type, false) return bgp.BGP_FSM_OPENCONFIRM, FSM_OPEN_MSG_RECEIVED } else { // send notification? h.conn.Close() return bgp.BGP_FSM_IDLE, FSM_INVALID_MSG } case *bgp.MessageError: fsm.sendNotificationFromErrorMsg(e.MsgData.(*bgp.MessageError)) return bgp.BGP_FSM_IDLE, FSM_INVALID_MSG default: log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state.String(), "Data": e.MsgData, }).Panic("unknown msg type") } case err := <-h.errorCh: h.conn.Close() return bgp.BGP_FSM_IDLE, err case <-holdTimer.C: fsm.sendNotification(bgp.BGP_ERROR_HOLD_TIMER_EXPIRED, 0, nil, "hold timer expired") h.t.Kill(nil) return bgp.BGP_FSM_IDLE, FSM_HOLD_TIMER_EXPIRED case s := <-fsm.adminStateCh: err := h.changeAdminState(s) if err == nil { switch s { case ADMIN_STATE_DOWN: h.conn.Close() return bgp.BGP_FSM_IDLE, FSM_ADMIN_DOWN case ADMIN_STATE_UP: log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state.String(), "AdminState": s.String(), }).Panic("code logic bug") } } } } }
func (h *FSMHandler) sendMessageloop() error { conn := h.conn fsm := h.fsm ticker := keepaliveTicker(fsm) send := func(m *bgp.BGPMessage) error { if fsm.twoByteAsTrans && m.Header.Type == bgp.BGP_MSG_UPDATE { log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state.String(), "Data": m, }).Debug("update for 2byte AS peer") table.UpdatePathAttrs2ByteAs(m.Body.(*bgp.BGPUpdate)) table.UpdatePathAggregator2ByteAs(m.Body.(*bgp.BGPUpdate)) } b, err := m.Serialize() if err != nil { log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state.String(), "Data": err, }).Warn("failed to serialize") fsm.bgpMessageStateUpdate(0, false) return nil } if err := conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(fsm.pConf.Timers.State.NegotiatedHoldTime))); err != nil { h.errorCh <- FSM_WRITE_FAILED conn.Close() return fmt.Errorf("failed to set write deadline") } _, err = conn.Write(b) if err != nil { log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state.String(), "Data": err, }).Warn("failed to send") h.errorCh <- FSM_WRITE_FAILED conn.Close() return fmt.Errorf("closed") } fsm.bgpMessageStateUpdate(m.Header.Type, false) switch m.Header.Type { case bgp.BGP_MSG_NOTIFICATION: log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state.String(), "Data": m, }).Warn("sent notification") body := m.Body.(*bgp.BGPNotification) h.errorCh <- FsmStateReason(fmt.Sprintf("%s %s", FSM_NOTIFICATION_SENT, bgp.NewNotificationErrorCode(body.ErrorCode, body.ErrorSubcode).String())) conn.Close() return fmt.Errorf("closed") case bgp.BGP_MSG_UPDATE: update := m.Body.(*bgp.BGPUpdate) log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state.String(), "nlri": update.NLRI, "withdrawals": update.WithdrawnRoutes, "attributes": update.PathAttributes, }).Debug("sent update") default: log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state.String(), "data": m, }).Debug("sent") } return nil } for { select { case <-h.t.Dying(): return nil case o := <-h.outgoing.Out(): m := o.(*FsmOutgoingMsg) for _, msg := range table.CreateUpdateMsgFromPaths(m.Paths) { if err := send(msg); err != nil { return nil } } if m.Notification != nil { if m.StayIdle { // current user is only prefix-limit // fix me if this is not the case h.changeAdminState(ADMIN_STATE_PFX_CT) } if err := send(m.Notification); err != nil { return nil } } case <-ticker.C: if err := send(bgp.NewBGPKeepAliveMessage()); err != nil { return nil } } } }
func keepalive() *bgp.BGPMessage { return bgp.NewBGPKeepAliveMessage() }