snaproute Go BGP Code Dive (9): Moving to Open

In the last session of snaproute BGP code dive—number 8, in fact— I started looking at how snaproute’s BGP moves from connect to open. This is the chain of calls from that post—

  • st.fsm.StopConnectRetryTimer()
  • st.fsm.SetPeerConn(data)
  • st.fsm.sendOpenMessage()
  • st.fsm.SetHoldTime(st.fsm.neighborConf.RunningConf.HoldTime, st.fsm.neighborConf.RunningConf.KeepaliveTime)
  • st.fsm.StartHoldTimer()
  • st.BaseState.fsm.ChangeState(NewOpenSentState(st.BaseState.fsm))

The past post covered the first two steps in this process, so this post will begin with the third step, st.fsm.sendOpenMessage(). Note the function call has in the front, so this is a call by reference. Each FSM that is spun up (think of them as threads, or even processes, if you must, to get this concept in your head, even though they’re not) can have its own copy of this function, with its own state. When reading the code to sort out how it works, this doesn’t have much practical impact, other than telling us the sendOpenMessage function we’re looking for is going to be in the FSM file. The function is located around line 1233 in fsm.go:

func (fsm *FSM) sendOpenMessage() {
  optParams := packet.ConstructOptParams(uint32(fsm.pConf.LocalAS), fsm.neighborConf.AfiSafiMap,
               fsm.neighborConf.RunningConf.AddPathsRx, fsm.neighborConf.RunningConf.AddPathsMaxTx)
  bgpOpenMsg := packet.NewBGPOpenMessage(fsm.pConf.LocalAS, uint16(fsm.holdTime), 
                fsm.gConf.RouterId.To4().String(), optParams) 
  packet, _ := bgpOpenMsg.Encode()
  num, err := (*fsm.peerConn.conn).Write(packet)
  if err != nil {

Each of these calls is fairly straightforward—

  • optParams := packet.ConstructOptParams builds a data structure which can be used to build an open packet. Notice the contruction involves such things as the AFI and AS number; these make sense if we're trying to build an open message to send to a new peer.
  • bgpOpenMsg := packet.NewBGPOpenMessage takes the data structure just built, adds some additional information, and actually builds a packet. If we were to look at this function, we'd find it takes this information and inserts it into the right TLVs, in the right order, to build a BGP open message.
  • packet, _ := bgpOpenMsg.Encode takes the set of TLVs and pushes any necessary headers, etc., onto the data structure to make it into an actual packet.
  • num, err := (*fsm.peerConn.conn).Write(packet) writes the packet to the TCP stream opened up way back in
    the fifth episode of this long running serial.

In the last step, note there is a check to make certain the packet was actually written to the TCP socket. If the packet is not written, an error is logged (I've removed the logging code as always to make following the actual chain of events simpler), and the startup process is aborted. Now that the packet is written, and hence in flight to the new peer, what happens? st.fsm.SetHoldTime, around line 1107 of fsm.go.

func (fsm *FSM) SetHoldTime(holdTime uint32, keepaliveTime uint32) {
  if holdTime < 0 || (holdTime > 0 && holdTime < 3) {

  fsm.holdTime = holdTime
  fsm.keepAliveTime = keepaliveTime

This code is fairly simple; it just checks to make certain the hold time for this new peer isn't being set to something less than 3 seconds, and then sets the hold time as configured. Once the hold timer is set, st.fsm.StartHoldTimer() actually starts the hold timer, around line 1120 of fsm.go

func (fsm *FSM) StartHoldTimer() {
  if fsm.holdTime != 0 {
    fsm.holdTimer.Reset(time.Duration(fsm.holdTime) * time.Second)  

Finally, st.BaseState.fsm.ChangeState(NewOpenSentState(st.BaseState.fsm)) simply sets the state machine for the peer that was just sent an open packet to the open state. What happens after the peer reaches the open state? We'll have to wait 'til next week to see how the snaproute BGP code moves from open to open confirmed, and then to established.