package server
import (
"bytes"
"fmt"
"math"
"net"
"time"
"github.com/bio-routing/bio-rd/routingtable"
"github.com/bio-routing/bio-rd/config"
tnet "github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/protocols/bgp/packet"
"github.com/bio-routing/bio-rd/route"
"github.com/bio-routing/bio-rd/routingtable/adjRIBIn"
"github.com/bio-routing/bio-rd/routingtable/adjRIBOut"
"github.com/bio-routing/bio-rd/routingtable/adjRIBOutAddPath"
"github.com/bio-routing/bio-rd/routingtable/locRIB"
log "github.com/sirupsen/logrus"
tomb "gopkg.in/tomb.v2"
)
const (
// Administrative events
ManualStart = 1
ManualStop = 2
AutomaticStart = 3
ManualStartWithPassiveTcpEstablishment = 4
AutomaticStartWithPassiveTcpEstablishment = 5
AutomaticStop = 8
// Timer events
ConnectRetryTimerExpires = 9
HoldTimerExpires = 10
KeepaliveTimerExpires = 11
)
const (
Cease = 0
Idle = 1
Connect = 2
Active = 3
OpenSent = 4
OpenConfirm = 5
Established = 6
)
type FSM struct {
peer *Peer
t tomb.Tomb
stateReason string
state int
lastState int
eventCh chan int
con *net.TCPConn
con2 *net.TCPConn
conCh chan *net.TCPConn
conErrCh chan error
initiateCon chan struct{}
passive bool
local net.IP
remote net.IP
localASN uint16
remoteASN uint16
neighborID uint32
routerID uint32
delayOpen bool
delayOpenTime time.Duration
delayOpenTimer *time.Timer
connectRetryTime time.Duration
connectRetryTimer *time.Timer
connectRetryCounter int
holdTimeConfigured time.Duration
holdTime time.Duration
holdTimer *time.Timer
keepaliveTime time.Duration
keepaliveTimer *time.Timer
msgRecvCh chan msgRecvMsg
msgRecvFailCh chan msgRecvErr
stopMsgRecvCh chan struct{}
adjRIBIn *adjRIBIn.AdjRIBIn
adjRIBOut routingtable.RouteTableClient
rib *locRIB.LocRIB
updateSender routingtable.RouteTableClient
capAddPathSend bool
capAddPathRecv bool
}
type msgRecvMsg struct {
msg []byte
con *net.TCPConn
}
type msgRecvErr struct {
err error
con *net.TCPConn
}
func NewFSM(peer *Peer, c config.Peer, rib *locRIB.LocRIB) *FSM {
fsm := &FSM{
peer: peer,
state: Idle,
passive: true,
connectRetryTime: 5,
connectRetryTimer: time.NewTimer(time.Second * time.Duration(20)),
msgRecvCh: make(chan msgRecvMsg),
msgRecvFailCh: make(chan msgRecvErr),
stopMsgRecvCh: make(chan struct{}),
holdTimeConfigured: time.Duration(c.HoldTimer),
holdTimer: time.NewTimer(0),
keepaliveTime: time.Duration(c.KeepAlive),
keepaliveTimer: time.NewTimer(0),
routerID: c.RouterID,
remote: c.PeerAddress,
local: c.LocalAddress,
localASN: uint16(c.LocalAS),
eventCh: make(chan int),
conCh: make(chan *net.TCPConn),
conErrCh: make(chan error), initiateCon: make(chan struct{}),
rib: rib,
}
return fsm
}
func (fsm *FSM) disconnect() {
if fsm.con != nil {
fsm.con.Close()
fsm.con = nil
}
if fsm.con2 != nil {
fsm.con2.Close()
fsm.con2 = nil
}
}
func (fsm *FSM) changeState(new int, reason string) int {
state := map[int]string{
Cease: "Cease",
Idle: "Idle",
Connect: "Connect",
Active: "Active",
OpenSent: "OpenSent",
OpenConfirm: "OpenConfirm",
Established: "Established",
}
log.WithFields(log.Fields{
"peer": fsm.remote.String(),
"last_state": state[fsm.state],
"new_state": state[new],
"reason": reason,
}).Info("FSM: Neighbor state change")
fsm.lastState = fsm.state
fsm.state = new
fsm.stateReason = reason
return fsm.state
}
func (fsm *FSM) activate() {
fsm.eventCh <- ManualStart
}
func (fsm *FSM) Stop() error {
fsm.eventCh <- ManualStop
fsm.t.Kill(nil)
return fsm.t.Wait()
}
func (fsm *FSM) start() {
fsm.t.Go(fsm.main)
fsm.t.Go(fsm.tcpConnector)
return
}
func (fsm *FSM) main() error {
next := fsm.idle()
for {
switch next {
case Cease:
fsm.t.Kill(fmt.Errorf("FSM is being stopped"))
return nil
case Idle:
next = fsm.idle()
case Connect:
next = fsm.connect()
case Active:
next = fsm.active()
case OpenSent:
next = fsm.openSent()
case OpenConfirm:
next = fsm.openConfirm()
case Established:
next = fsm.established()
}
}
}
func (fsm *FSM) idle() int {
if fsm.adjRIBOut != nil {
fsm.rib.Unregister(fsm.adjRIBOut)
fsm.adjRIBOut.Unregister(fsm.updateSender)
}
fsm.adjRIBIn = nil
fsm.adjRIBOut = nil
for {
select {
case c := <-fsm.conCh:
c.Close()
continue
case e := <-fsm.eventCh:
reason := ""
switch e {
case ManualStart:
reason = "Received ManualStart event %d for %s peer"
case AutomaticStart:
reason = "Received AutomaticStart event %d for %s peer"
default:
continue
}
fsm.connectRetryCounter = 0
fsm.startConnectRetryTimer()
if fsm.passive {
return fsm.changeState(Active, fmt.Sprintf(reason, "passive"))
}
fsm.tcpConnect()
return fsm.changeState(Connect, fmt.Sprintf(reason, "active"))
}
}
}
func (fsm *FSM) tcpConnector() error {
for {
select {
case <-fsm.initiateCon:
c, err := net.DialTCP("tcp", &net.TCPAddr{IP: fsm.local}, &net.TCPAddr{IP: fsm.remote, Port: BGPPORT})
if err != nil {
select {
case fsm.conErrCh <- err:
continue
case <-time.NewTimer(time.Second * 30).C:
continue
}
}
select {
case fsm.conCh <- c:
continue
case <-time.NewTimer(time.Second * 30).C:
c.Close()
continue
}
case <-fsm.t.Dying():
return nil
}
}
}
func (fsm *FSM) tcpConnect() {
fsm.initiateCon <- struct{}{}
}
// connect state waits for a TCP connection to be fully established. Either the active or passive one.
func (fsm *FSM) connect() int {
for {
select {
case e := <-fsm.eventCh:
if e == ManualStop {
fsm.connectRetryCounter = 0
stopTimer(fsm.connectRetryTimer)
return fsm.changeState(Idle, "Manual stop event")
}
continue
case <-fsm.connectRetryTimer.C:
fsm.resetConnectRetryTimer()
fsm.tcpConnect()
continue
case c := <-fsm.conCh:
fsm.con = c
stopTimer(fsm.connectRetryTimer)
return fsm.connectSendOpen()
}
}
}
func (fsm *FSM) connectSendOpen() int {
err := fsm.sendOpen(fsm.con)
if err != nil {
stopTimer(fsm.connectRetryTimer)
return fsm.changeState(Idle, fmt.Sprintf("Sending OPEN message failed: %v", err))
}
fsm.holdTimer = time.NewTimer(time.Minute * 4)
return fsm.changeState(OpenSent, "Sent OPEN message")
}
// in the active state we wait for a passive TCP connection to be established
func (fsm *FSM) active() int {
for {
select {
case e := <-fsm.eventCh:
if e == ManualStop {
fsm.disconnect()
fsm.connectRetryCounter = 0
stopTimer(fsm.connectRetryTimer)
return fsm.changeState(Active, "Manual stop event")
}
continue
case <-fsm.connectRetryTimer.C:
fsm.resetConnectRetryTimer()
fsm.tcpConnect()
return fsm.changeState(Connect, "Connect retry timer expired")
case c := <-fsm.conCh:
fsm.con = c
stopTimer(fsm.connectRetryTimer)
return fsm.activeSendOpen()
}
}
}
func (fsm *FSM) activeSendOpen() int {
err := fsm.sendOpen(fsm.con)
if err != nil {
fsm.resetConnectRetryTimer()
fsm.connectRetryCounter++
return fsm.changeState(Idle, fmt.Sprintf("Sending OPEN message failed: %v", err))
}
fsm.holdTimer = time.NewTimer(time.Minute * 4)
return fsm.changeState(OpenSent, "Sent OPEN message")
}
func (fsm *FSM) msgReceiver(c *net.TCPConn) error {
for {
msg, err := recvMsg(c)
if err != nil {
fsm.msgRecvFailCh <- msgRecvErr{err: err, con: c}
return nil
/*select {
case fsm.msgRecvFailCh <- msgRecvErr{err: err, con: c}:
continue
case <-time.NewTimer(time.Second * 60).C:
return nil
}*/
}
fsm.msgRecvCh <- msgRecvMsg{msg: msg, con: c}
select {
case <-fsm.stopMsgRecvCh:
return nil
default:
continue
}
}
}
func (fsm *FSM) openSent() int {
go fsm.msgReceiver(fsm.con)
for {
select {
case e := <-fsm.eventCh:
if e == ManualStop {
sendNotification(fsm.con, packet.Cease, 0)
stopTimer(fsm.connectRetryTimer)
fsm.disconnect()
fsm.connectRetryCounter = 0
return fsm.changeState(Idle, "Manual stop event")
}
continue
case <-fsm.holdTimer.C:
sendNotification(fsm.con, packet.HoldTimeExpired, 0)
stopTimer(fsm.connectRetryTimer)
fsm.disconnect()
fsm.connectRetryCounter++
return fsm.changeState(Idle, "Holdtimer expired")
case c := <-fsm.conCh: // 2nd connection coming in. Collision!
if fsm.con2 != nil {
log.WithFields(log.Fields{
"peer": fsm.remote.String(),
"local": fsm.local.String(),
}).Warningf("Received third connection from peer. Dropping new connection")
c.Close()
continue
}
err := fsm.sendOpen(c) // FIXME: Not sure if this is standard compliant
if err != nil {
c.Close()
continue
}
fsm.con2 = c
go fsm.msgReceiver(c)
continue
case recvMsg := <-fsm.msgRecvCh:
msg, err := packet.Decode(bytes.NewBuffer(recvMsg.msg))
if err != nil {
switch bgperr := err.(type) {
case packet.BGPError:
sendNotification(fsm.con, bgperr.ErrorCode, bgperr.ErrorSubCode)
sendNotification(fsm.con2, bgperr.ErrorCode, bgperr.ErrorSubCode)
}
stopTimer(fsm.connectRetryTimer)
fsm.disconnect()
fsm.connectRetryCounter++
return fsm.changeState(Idle, fmt.Sprintf("Failed to decode BGP message: %v", err))
}
switch msg.Header.Type {
case packet.NotificationMsg:
nMsg := msg.Body.(*packet.BGPNotification)
if nMsg.ErrorCode == packet.UnsupportedVersionNumber {
stopTimer(fsm.connectRetryTimer)
fsm.disconnect()
return fsm.changeState(Idle, "Received NOTIFICATION")
}
if nMsg.ErrorCode == packet.Cease {
// Was this connection to be closed anyways?
if fsm.dumpCon(recvMsg.con) {
continue
}
}
stopTimer(fsm.connectRetryTimer)
fsm.disconnect()
fsm.connectRetryCounter++
return fsm.changeState(Idle, "Received NOTIFICATION")
case packet.OpenMsg:
openMsg := msg.Body.(*packet.BGPOpen)
fsm.neighborID = openMsg.BGPIdentifier
fsm.resolveCollision()
stopTimer(fsm.connectRetryTimer)
err := fsm.sendKeepalive()
if err != nil {
return fsm.openSentTCPFail(err)
}
fsm.holdTime = time.Duration(math.Min(float64(fsm.holdTimeConfigured), float64(openMsg.HoldTime)))
if fsm.holdTime != 0 {
fsm.holdTimer.Reset(time.Second * fsm.holdTime)
fsm.keepaliveTime = fsm.holdTime / 3
fsm.keepaliveTimer.Reset(time.Second * fsm.keepaliveTime)
}
fsm.processOpenOptions(openMsg.OptParams)
return fsm.changeState(OpenConfirm, "Received OPEN message")
default:
sendNotification(fsm.con, packet.FiniteStateMachineError, 0)
stopTimer(fsm.connectRetryTimer)
fsm.con.Close()
fsm.connectRetryCounter++
return fsm.changeState(Idle, "FSM Error")
}
case err := <-fsm.msgRecvFailCh:
if err.con == fsm.con && fsm.con2 != nil {
fsm.con.Close()
fsm.con = fsm.con2
fsm.con2 = nil
continue
}
if err.con == fsm.con2 {
fsm.con2.Close()
fsm.con2 = nil
continue
}
return fsm.openSentTCPFail(err.err)
}
}
}
func (fsm *FSM) processOpenOptions(optParams []packet.OptParam) {
for _, optParam := range optParams {
if optParam.Type != packet.CapabilitiesParamType {
continue
}
fsm.processCapabilities(optParam.Value.(packet.Capabilities))
}
}
func (fsm *FSM) processCapabilities(caps packet.Capabilities) {
for _, cap := range caps {
fsm.processCapability(cap)
}
}
func (fsm *FSM) processCapability(cap packet.Capability) {
switch cap.Code {
case packet.AddPathCapabilityCode:
fsm.processAddPathCapability(cap.Value.(packet.AddPathCapability))
}
}
func (fsm *FSM) processAddPathCapability(addPathCap packet.AddPathCapability) {
if addPathCap.AFI != 1 {
return
}
if addPathCap.SAFI != 1 {
return
}
switch addPathCap.SendReceive {
case packet.AddPathReceive:
if !fsm.peer.addPathSend.BestOnly {
fsm.capAddPathSend = true
}
case packet.AddPathSend:
if fsm.peer.addPathRecv {
fsm.capAddPathRecv = true
}
case packet.AddPathSendReceive:
if !fsm.peer.addPathSend.BestOnly {
fsm.capAddPathSend = true
}
if fsm.peer.addPathRecv {
fsm.capAddPathRecv = true
}
}
}
func (fsm *FSM) openSentTCPFail(err error) int {
fsm.con.Close()
fsm.resetConnectRetryTimer()
return fsm.changeState(Active, fmt.Sprintf("TCP failure: %v", err))
}
func (fsm *FSM) dumpCon(c *net.TCPConn) bool {
p := fsm.isPassive(c)
if fsm.routerID > fsm.neighborID {
return p
}
return !p
}
func (fsm *FSM) resolveCollision() {
if fsm.con2 == nil {
return
}
if fsm.routerID > fsm.neighborID {
// Terminate passive connection
if fsm.isPassive(fsm.con) {
dumpCon(fsm.con)
fsm.con = fsm.con2
return
}
if fsm.isPassive(fsm.con2) {
dumpCon(fsm.con2)
return
}
return
}
// Terminate active connection
if !fsm.isPassive(fsm.con) {
dumpCon(fsm.con)
fsm.con = fsm.con2
return
}
if !fsm.isPassive(fsm.con2) {
dumpCon(fsm.con2)
fsm.con2.Close()
fsm.con2 = nil
return
}
}
func dumpCon(c *net.TCPConn) {
sendNotification(c, packet.Cease, packet.ConnectionCollisionResolution)
c.Close()
}
func (fsm *FSM) isPassive(c *net.TCPConn) bool {
if c.LocalAddr().String() == fmt.Sprintf("%s:179", fsm.local.String()) {
return true
}
return false
}
func (fsm *FSM) openConfirm() int {
for {
select {
case e := <-fsm.eventCh:
if e == ManualStop { // Event 2
sendNotification(fsm.con, packet.Cease, 0)
stopTimer(fsm.connectRetryTimer)
fsm.disconnect()
fsm.connectRetryCounter = 0
return fsm.changeState(Idle, "Manual stop event")
}
continue
case <-fsm.holdTimer.C:
sendNotification(fsm.con, packet.HoldTimeExpired, 0)
stopTimer(fsm.connectRetryTimer)
fsm.disconnect()
fsm.connectRetryCounter++
return fsm.changeState(Idle, "Holdtimer expired")
case <-fsm.keepaliveTimer.C:
err := fsm.sendKeepalive()
if err != nil {
stopTimer(fsm.connectRetryTimer)
fsm.disconnect()
fsm.connectRetryCounter++
return fsm.changeState(Idle, fmt.Sprintf("Failed to send keepalive: %v", err))
}
fsm.keepaliveTimer.Reset(time.Second * fsm.keepaliveTime)
continue
case c := <-fsm.conCh:
if fsm.con2 != nil {
log.WithFields(log.Fields{
"peer": fsm.remote.String(),
"local": fsm.local.String(),
}).Warningf("Received third connection from peer. Dropping new connection")
c.Close()
continue
}
err := fsm.sendOpen(c) // FIXME: Not sure if this is standard compliant
if err != nil {
c.Close()
continue
}
fsm.con2 = c
go fsm.msgReceiver(c)
continue
case recvMsg := <-fsm.msgRecvCh:
msg, err := packet.Decode(bytes.NewBuffer(recvMsg.msg))
if err != nil {
fmt.Printf("Failed to decode message: %v\n", recvMsg.msg)
switch bgperr := err.(type) {
case packet.BGPError:
sendNotification(fsm.con, bgperr.ErrorCode, bgperr.ErrorSubCode)
sendNotification(fsm.con2, bgperr.ErrorCode, bgperr.ErrorSubCode)
}
stopTimer(fsm.connectRetryTimer)
fsm.disconnect()
fsm.connectRetryCounter++
return fsm.changeState(Idle, "Failed to decode BGP message")
}
switch msg.Header.Type {
case packet.NotificationMsg:
nMsg := msg.Body.(*packet.BGPNotification)
if nMsg.ErrorCode == packet.UnsupportedVersionNumber {
stopTimer(fsm.connectRetryTimer)
fsm.con.Close()
return fsm.changeState(Idle, "Received NOTIFICATION")
}
if nMsg.ErrorCode == packet.Cease {
// Was this connection to be closed anyways?
if fsm.dumpCon(recvMsg.con) {
continue
}
}
return fsm.openConfirmTCPFail(fmt.Errorf("NOTIFICATION received"))
case packet.KeepaliveMsg:
fsm.holdTimer.Reset(time.Second * fsm.holdTime)
return fsm.changeState(Established, "Received KEEPALIVE")
case packet.OpenMsg:
openMsg := msg.Body.(*packet.BGPOpen)
fsm.neighborID = openMsg.BGPIdentifier
fsm.resolveCollision()
default:
sendNotification(fsm.con, packet.FiniteStateMachineError, 0)
stopTimer(fsm.connectRetryTimer)
fsm.con.Close()
fsm.connectRetryCounter++
return fsm.changeState(Idle, "FSM Error")
}
case err := <-fsm.msgRecvFailCh:
if err.con == fsm.con && fsm.con2 != nil {
fsm.con.Close()
fsm.con = fsm.con2
fsm.con2 = nil
continue
}
if err.con == fsm.con2 {
fsm.con2.Close()
fsm.con2 = nil
continue
}
return fsm.openConfirmTCPFail(err.err)
}
}
}
func (fsm *FSM) openConfirmTCPFail(err error) int {
fsm.con.Close()
fsm.resetConnectRetryTimer()
fsm.connectRetryCounter++
return fsm.changeState(Idle, fmt.Sprintf("Failure: %v", err))
}
func (fsm *FSM) established() int {
fsm.adjRIBIn = adjRIBIn.New()
fsm.adjRIBIn.Register(fsm.rib)
n := &routingtable.Neighbor{
Type: route.BGPPathType,
Address: tnet.IPv4ToUint32(fsm.remote),
}
clientOptions := routingtable.ClientOptions{}
if fsm.capAddPathSend {
fsm.updateSender = newUpdateSenderAddPath(fsm)
fsm.adjRIBOut = adjRIBOutAddPath.New(n)
clientOptions = fsm.peer.addPathSend
} else {
fsm.updateSender = newUpdateSender(fsm)
fsm.adjRIBOut = adjRIBOut.New(n)
}
fsm.adjRIBOut.Register(fsm.updateSender)
fsm.rib.RegisterWithOptions(fsm.adjRIBOut, clientOptions)
/*go func() {
for {
if fsm.adjRIBOut == nil {
return
}
fmt.Printf("ADJ-RIB-OUT: %s\n", fsm.remote.String())
fmt.Print(fsm.adjRIBOut.Print())
time.Sleep(time.Second * 11)
}
}()*/
for {
select {
case e := <-fsm.eventCh:
if e == ManualStop { // Event 2
sendNotification(fsm.con, packet.Cease, 0)
stopTimer(fsm.connectRetryTimer)
fsm.con.Close()
fsm.connectRetryCounter = 0
return fsm.changeState(Idle, "Manual stop event")
}
if e == AutomaticStop { // Event 8
sendNotification(fsm.con, packet.Cease, 0)
stopTimer(fsm.connectRetryTimer)
fsm.con.Close()
fsm.connectRetryCounter++
return fsm.changeState(Idle, "Automatic stop event")
}
continue
case <-fsm.holdTimer.C:
sendNotification(fsm.con, packet.HoldTimeExpired, 0)
stopTimer(fsm.connectRetryTimer)
fsm.con.Close()
fsm.connectRetryCounter++
return fsm.changeState(Idle, "Holdtimer expired")
case <-fsm.keepaliveTimer.C:
err := fsm.sendKeepalive()
if err != nil {
stopTimer(fsm.connectRetryTimer)
fsm.con.Close()
fsm.connectRetryCounter++
return fsm.changeState(Idle, fmt.Sprintf("Failed to send keepalive: %v", err))
}
fsm.keepaliveTimer.Reset(time.Second * fsm.keepaliveTime)
continue
case c := <-fsm.conCh:
c.Close()
continue
case recvMsg := <-fsm.msgRecvCh:
msg, err := packet.Decode(bytes.NewBuffer(recvMsg.msg))
if err != nil {
fmt.Printf("Failed to decode BGP message: %v\n", recvMsg.msg)
switch bgperr := err.(type) {
case packet.BGPError:
sendNotification(fsm.con, bgperr.ErrorCode, bgperr.ErrorSubCode)
}
stopTimer(fsm.connectRetryTimer)
fsm.con.Close()
fsm.connectRetryCounter++
return fsm.changeState(Idle, "Failed to decode BGP message")
}
switch msg.Header.Type {
case packet.NotificationMsg:
stopTimer(fsm.connectRetryTimer)
fsm.con.Close()
fsm.connectRetryCounter++
return fsm.changeState(Idle, "Received NOTIFICATION")
case packet.UpdateMsg:
if fsm.holdTime != 0 {
fsm.holdTimer.Reset(time.Second * fsm.holdTime)
}
u := msg.Body.(*packet.BGPUpdate)
for r := u.WithdrawnRoutes; r != nil; r = r.Next {
pfx := tnet.NewPfx(r.IP, r.Pfxlen)
fmt.Printf("LPM: Removing prefix %s\n", pfx.String())
fsm.adjRIBIn.RemovePath(pfx, nil)
}
for r := u.NLRI; r != nil; r = r.Next {
pfx := tnet.NewPfx(r.IP, r.Pfxlen)
fmt.Printf("LPM: Adding prefix %s\n", pfx.String())
path := &route.Path{
Type: route.BGPPathType,
BGPPath: &route.BGPPath{
Source: tnet.IPv4ToUint32(fsm.remote),
},
}
for pa := u.PathAttributes; pa != nil; pa = pa.Next {
fmt.Printf("TypeCode: %d\n", pa.TypeCode)
switch pa.TypeCode {
case packet.OriginAttr:
path.BGPPath.Origin = pa.Value.(uint8)
case packet.LocalPrefAttr:
path.BGPPath.LocalPref = pa.Value.(uint32)
case packet.MEDAttr:
path.BGPPath.MED = pa.Value.(uint32)
case packet.NextHopAttr:
fmt.Printf("RECEIVED NEXT_HOP: %d\n", pa.Value.(uint32))
path.BGPPath.NextHop = pa.Value.(uint32)
case packet.ASPathAttr:
path.BGPPath.ASPath = pa.ASPathString()
path.BGPPath.ASPathLen = pa.ASPathLen()
}
}
fsm.adjRIBIn.AddPath(pfx, path)
}
continue
case packet.KeepaliveMsg:
if fsm.holdTime != 0 {
fsm.holdTimer.Reset(time.Second * fsm.holdTime)
}
continue
case packet.OpenMsg:
if fsm.con2 != nil {
sendNotification(fsm.con2, packet.Cease, packet.ConnectionCollisionResolution)
fsm.con2.Close()
fsm.con2 = nil
continue
}
sendNotification(fsm.con, packet.FiniteStateMachineError, 0)
stopTimer(fsm.connectRetryTimer)
fsm.con.Close()
fsm.connectRetryCounter++
return fsm.changeState(Idle, "FSM Error")
default:
sendNotification(fsm.con, packet.FiniteStateMachineError, 0)
stopTimer(fsm.connectRetryTimer)
fsm.con.Close()
fsm.connectRetryCounter++
return fsm.changeState(Idle, "FSM Error")
}
case err := <-fsm.msgRecvFailCh:
if err.con == fsm.con && fsm.con2 != nil {
fsm.con.Close()
fsm.con = fsm.con2
fsm.con2 = nil
continue
}
if err.con == fsm.con2 {
fsm.con2.Close()
fsm.con2 = nil
continue
}
return fsm.openConfirmTCPFail(err.err)
}
}
}
func stopTimer(t *time.Timer) {
if !t.Stop() {
select {
case <-t.C:
default:
}
}
}
func (fsm *FSM) startConnectRetryTimer() {
fsm.connectRetryTimer = time.NewTimer(time.Second * fsm.connectRetryTime)
}
func (fsm *FSM) resetConnectRetryTimer() {
if !fsm.connectRetryTimer.Reset(time.Second * fsm.connectRetryTime) {
<-fsm.connectRetryTimer.C
}
}
func (fsm *FSM) resetDelayOpenTimer() {
if !fsm.delayOpenTimer.Reset(time.Second * fsm.delayOpenTime) {
<-fsm.delayOpenTimer.C
}
}
func (fsm *FSM) sendKeepalive() error {
msg := packet.SerializeKeepaliveMsg()
_, err := fsm.con.Write(msg)
if err != nil {
return fmt.Errorf("Unable to send KEEPALIVE message: %v", err)
}
return nil
}
func (fsm *FSM) sendOpen(c *net.TCPConn) error {
msg := packet.SerializeOpenMsg(&packet.BGPOpen{
Version: BGPVersion,
AS: fsm.localASN,
HoldTime: uint16(fsm.holdTimeConfigured),
BGPIdentifier: fsm.routerID,
OptParams: fsm.peer.optOpenParams,
})
_, err := c.Write(msg)
if err != nil {
return fmt.Errorf("Unable to send OPEN message: %v", err)
}
return nil
}
func sendNotification(c *net.TCPConn, errorCode uint8, errorSubCode uint8) error {
if c == nil {
return fmt.Errorf("connection is nil")
}
msg := packet.SerializeNotificationMsg(&packet.BGPNotification{})
_, err := c.Write(msg)
if err != nil {
return fmt.Errorf("Unable to send NOTIFICATION message: %v", err)
}
return nil
}