BGP Deep Dive Part 2: The Session, From TCP SYN to Established

Part 1 covered why BGP exists and what problem it’s solving. This post is where that stops being theory and starts being a protocol: what two routers actually do, message by message, between “these are configured as peers” and “routes are flowing.”

Why BGP needs a transport layer at all

OSPF and IS-IS run directly over the network layer, IP and the raw link layer respectively, because they only ever talk to a neighbor that’s one hop away on a shared link. BGP doesn’t have that luxury. An eBGP session is usually directly connected too, but iBGP sessions routinely run between routers that are many hops apart inside the same AS, and multihop eBGP is a normal, supported configuration for peering that doesn’t happen over a shared segment at all. A protocol that can’t assume link-local adjacency needs something to handle reliable delivery, sequencing, and retransmission across an arbitrary number of hops, and rather than reinvent that, BGP just uses TCP.

BGP sessions run over TCP port 179. Whichever side initiates gets an ephemeral source port and connects to 179 on the peer; most implementations will actually attempt the connection in both directions simultaneously and rely on the state machine, not manual configuration, to resolve which attempt wins. Riding on TCP means BGP inherits reliable, in-order delivery for free, and it also means a BGP session, unlike an OSPF adjacency, can be affected by anything that affects a normal TCP connection: path MTU issues, asymmetric routing, a stateful firewall in the middle deciding the session is idle and aging out its conntrack entry.

The finite state machine

RFC 4271 defines BGP’s session establishment as a six-state finite state machine. It’s worth walking through properly, because most of what looks like a mysterious BGP flap in a log file is just this machine bouncing between two of these states.

Idle is the starting point and the state a session falls back to after most kinds of failure. In Idle, a BGP speaker isn’t trying to do anything: no TCP connection is attempted, no resources beyond configuration are allocated. A ManualStart event, in practice just the peer being configured and enabled, initializes the ConnectRetryCounter and moves the session to Connect.

Connect is the state where the speaker is actively trying to establish the TCP connection, waiting on the three-way handshake to complete. If the TCP connection succeeds, the session moves to OpenSent. If it fails, or the ConnectRetryTimer expires first, the session moves to Active instead, not back to Idle, which is a distinction worth remembering when reading a state history: Active isn’t a good sign, it means the previous attempt already failed once.

Active is where the speaker keeps retrying the TCP connection. A session sitting in Active is trying, not established, and a session stuck cycling between Connect and Active without ever reaching OpenSent is telling you the TCP handshake itself isn’t completing, which is a Layer 3/4 reachability problem, not a BGP configuration problem, almost every time.

OpenSent is reached once the TCP connection is up and the speaker has sent its own OPEN message. It’s now waiting for the peer’s OPEN in return. When it arrives, the speaker checks it: BGP version, the peer’s AS number matches what’s configured, the proposed hold time is acceptable, and that the BGP Identifier is valid. Any of those checks failing sends a NOTIFICATION message back and drops the session to Idle. If the checks pass, a KEEPALIVE is sent and the session moves to OpenConfirm.

OpenConfirm is the last stage before routes can flow. Both sides have exchanged and validated OPEN messages; each is now waiting to receive a KEEPALIVE from the other to confirm the session is genuinely bidirectional and healthy, not just that a TCP handshake and one OPEN each happened to succeed. Receiving that KEEPALIVE moves the session to Established.

Established is the working state. This is the only state in which UPDATE messages, the ones actually carrying routes, are permitted. Everything before this point was negotiation; this is where the two speakers start exchanging NLRI and path attributes, and where the rest of this series’ content actually happens on the wire.

A session can drop out of Established back to Idle at any point: a hold timer expiring with no KEEPALIVE or UPDATE received, a NOTIFICATION received or sent for a protocol error, the underlying TCP connection resetting. When that happens, most implementations don’t retry immediately. There’s a deliberate backoff, both to avoid hammering a peer that’s actually down and to give whatever caused the drop time to clear, and this is genuinely one of the more common sources of “why did it take 30 seconds to come back up” confusion in the field.

What the OPEN message actually negotiates

The OPEN message is BGP’s handshake proper, and its fields are where the session’s actual parameters get proposed and checked, not assumed:

  • Version: always 4 on the modern internet. BGP-1 through BGP-3 are historical at this point.
  • My Autonomous System: the sender’s ASN, in a 2-octet field. This is the field that produces one of the odder-looking things you’ll see in a packet capture of a 4-byte-ASN session: the field is populated with AS_TRANS, 23456, a reserved placeholder value, and the sender’s real 32-bit ASN is instead carried in an optional capability. That’s how 4-byte AS number support, from RFC 6793, stays backward compatible with speakers that only understand the original 2-octet field.
  • Hold Time: the sender’s proposed hold timer, in seconds. Both sides propose a value, and per RFC 4271 the actual hold timer used for the session is the lower of the two. A speaker with a longer configured hold time doesn’t get to insist on it if its peer proposes shorter.
  • BGP Identifier: a 4-octet value, formatted as and usually derived from an IPv4 address, that uniquely identifies the speaker. It’s used as an opaque 32-bit identifier even on IPv6-only sessions, which is a detail that trips people up the first time they configure BGP on a router with no IPv4 addressing anywhere: something still has to supply a router ID.
  • Optional Parameters: a variable-length field carrying capabilities, and this is where most of what makes modern BGP modern actually lives.

Capabilities: the OPEN message’s extension mechanism

RFC 5492 defines the Capabilities optional parameter, and without it BGP would still be the address-family-agnostic, IPv4-only protocol it was in the mid-90s. Capabilities are how two speakers advertise and negotiate optional behavior during the OPEN exchange, before either side commits to using it. The ones that matter most for the rest of this series:

  • Multiprotocol Extensions (RFC 4760): advertises which address families, AFI/SAFI pairs, a speaker is willing to carry NLRI for beyond plain unicast IPv4. This is the capability that makes IPv6, VPNv4, and EVPN possible over the same session type, covered properly in Part 10.
  • Route Refresh: lets a speaker ask its peer to resend its full route table without tearing the session down, useful after a policy change where the alternative used to be bouncing the session.
  • 4-octet AS Number (RFC 6793): the capability that carries a speaker’s real ASN when the My Autonomous System field is holding AS_TRANS.
  • Graceful Restart: signals that a speaker can preserve forwarding state across a control-plane restart, so a peer doesn’t need to immediately withdraw all its routes. Covered properly in Part 11.

The Optional Parameters field was originally a single octet, meaning its length was capped at 255 octets total, and a fully loaded modern router negotiating several address families plus 4-byte ASNs plus graceful restart plus add-path can genuinely run into that ceiling. RFC 9072, published in 2021, added an extended optional parameters format specifically to raise that cap, which is a reasonable proxy for how much capability negotiation has grown since 1995’s original design.

Keeping the session alive

Once Established, the two speakers keep the session up with KEEPALIVE messages, sent at an interval that’s conventionally one third of the negotiated hold time. If a full hold time interval passes with no KEEPALIVE and no UPDATE received from a peer, the session is declared dead and drops to Idle. The specific numbers are a configuration choice, not something RFC 4271 mandates, but 60 seconds keepalive against 180 seconds hold time has become the de facto default across most vendor implementations, Cisco’s included, closely enough that it’s worth knowing as a baseline before you see a vendor’s actual default in Part 7 through 9’s implementation posts. A hold time of zero is a valid special case: it means no keepalives are exchanged at all, which shows up in some monitoring or route-collector style sessions where the entire point is a passive, unmonitored feed.

It’s worth flagging early, since Part 11 covers it properly: hold timer expiry is a slow failure-detection mechanism by modern standards, tens of seconds, and BFD exists specifically to give BGP a sub-second alternative that doesn’t wait on the protocol’s own timers.

Securing the session itself

None of the above stops someone who can inject packets into the TCP stream between two peers from disrupting or spoofing a session. The original mitigation, still the most commonly deployed one, is the TCP MD5 Signature option from RFC 2385: a shared secret used to sign every TCP segment on the session, checked on receipt, with mismatched or missing signatures dropped. It works, but MD5 itself is a weak hash by current standards and the option has no support for key rotation without a service-affecting change.

TCP Authentication Option, TCP-AO, from RFC 5925, was designed as its proper replacement: modern algorithms, in-band key rotation without dropping the session, and protection against a wider set of attacks. Adoption has lagged well behind the specification’s age, largely because vendor support for TCP-AO came later and less universally than MD5’s had decades earlier, and most production BGP sessions today are still secured with plain MD5 if they’re secured at all. That gap between the better spec and what’s actually deployed is a pattern this series will come back to more than once.

Part 3 moves past session establishment into what actually gets sent once a session is Established: the UPDATE message, and the full catalog of path attributes that carry the policy Part 1 argued is the whole point of BGP in the first place.