snaproute Go BGP Code Dive (11): Moving to Open Confirm

In the last post in this series, we began considering the bgp code that handles the open message that begins moving a new peer to open confirmed state. This is the particular bit of code of interest—

case BGPEventBGPOpen:
  st.fsm.StopConnectRetryTimer()
  bgpMsg := data.(*packet.BGPMessage)
  if st.fsm.ProcessOpenMessage(bgpMsg) {
    st.fsm.sendKeepAliveMessage()
    st.fsm.StartHoldTimer()
    st.fsm.ChangeState(NewOpenConfirmState(st.fsm))
  }

We looked at how this code assigns the contents of the received packet to bgpMsg; now we need to look at how this information is actually processed. bgpMsg is passed to st.fsm.ProcessOpenMessage() in the next line. This call is preceded by the st.fsm, which means this function is going to be found in the FSM, which means fsm.go. Indeed, func (fsm *FSM) ProcessOpenMessage... is around line 1172 in fsm.go—

func (fsm *FSM) ProcessOpenMessage(pkt *packet.BGPMessage) bool {
 body := pkt.Body.(*packet.BGPOpen)

 if uint32(body.HoldTime) < fsm.holdTime {
  fsm.SetHoldTime(uint32(body.HoldTime), uint32(body.HoldTime/3))
 }

 if body.MyAS == fsm.Manager.gConf.AS {
  fsm.peerType = config.PeerTypeInternal—
 } else {
  fsm.peerType = config.PeerTypeExternal
 }

 afiSafiMap := packet.GetProtocolFromOpenMsg(body)
 for protoFamily, _ := range afiSafiMap {
  if fsm.neighborConf.AfiSafiMap
[protoFamily] { fsm.afiSafiMap[protoFamily] = true } } return fsm.Manager.receivedBGPOpenMessage(fsm.id, fsm.peerConn.dir, body) }

There are three “sections” in this function, each one takes care of a different thing. The first section—

if uint32(body.HoldTime) < fsm.holdTime {
 fsm.SetHoldTime(uint32(body.HoldTime), uint32(body.HoldTime/3))
}

This is fairly simple; it compares the received hold time with the locally configured hold time, setting the final hold time to the lower of these two numbers. This is in line with the most recent BGP specification (RFC 4271), section 4.2, which states—

This 2-octet unsigned integer indicates the number of seconds the sender proposes for the value of the Hold Timer. Upon receipt of an OPEN message, 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.

The second section of this code is a little more confusing—

if body.MyAS == fsm.Manager.gConf.AS {
 fsm.peerType = config.PeerTypeInternal
} else {
 fsm.peerType = config.PeerTypeExternal
}

This obviously somehow sets the type of peer, internal (iBGP) or external (eBGP), but how precisely does this work? The if statement is the crucial point here; if the statement is true, then first branch is executed, which sets the peer type to iBGP. If the <codeif statement evaluates as !true, the second branch is executed, setting the peer type to eBGP.

Note the difference between = and ==. In both C and Go, = assigns the value or the contents of the variable on the right side of the = to the variable on the left side. The == operator compares the two values, returning a 0 if the values (or contents of the two variables) are the same, and 0 if the values (or contents of the two variables) does not match.

The if statement itself is comparing body.MyAS to fsm.Manager.gConf.AS; what do these contain? body.MyAS is an element of the body structure, which is taken from the packet contents at the beginning of the function by the line body := pkt.Body.(*packet.BGPOpen). body.MyAS, is, then the AS number of the remote peer. On the other hand, fsm.Manager.gConf.AS is being taken from the local fsm state, in particular the configuration state for the local peer process. Given these two definitions, these lines of code make sense; if the local and remote AS match, then the neighbor type should be set to iBGP. If they don’t match, then the neighbor type should be set to eBGP.

The final section of code is the most complex of the three—

afiSafiMap := packet.GetProtocolFromOpenMsg(body)
for protoFamily, _ := range afiSafiMap {
 if fsm.neighborConf.AfiSafiMap[protoFamily] {
  fsm.afiSafiMap[protoFamily] = true
 }

The first line of code here grabs a list of the address families (AFIs)/subaddress families (SAFIs) supported by the peer, as reported in its open message and places them into a list. The second line of code, for protoFamily, _ := range afiSafiMap {, walks through a list of each possible protocol family, checking each one to see if it’s included in the peer’s list of AFIs/SAFIs. If a particular AFI/SAFI is included in the peer’s supported list, then the AFI/SAFI is set to true, which will serve as an indicator to any other process interacting with this particular peer which specific AFIs/SAFIs are supported.

At this point, the open message received from the new peer has been processed. Once ProcessOpenMessage finishes, it will return to the main FSM, and to the remainder of the switch statement above.

st.fsm.sendKeepAliveMessage() will now send the first TCP keepalive to this new peer; as there is no timer for sending keepalive messages set at this point, and there is no way to tell how long processing the open message has taken, the safest thing to do is to send this first keepalive message.

st.fsm.StartHoldTimer() will now start a hold timer. If this timer expires, the peer will be brought down—this is something look at later, when we consider various error conditions the various bits of code might encounter, and the expiration (waking up) of various timers set along the way.

Finally, st.fsm.ChangeState(NewOpenConfirmState(st.fsm)) sets the current state to open confirm, bringing us one step closer to exchanging databases, and transitioning this new peer into the normal state for BGP neighbors.

We’ll consider the next step in this process in the next code dive.

Tags: , |