Every route on the internet that crosses from one network to another was carried there by BGP, one TCP conversation at a time. This chapter opens those conversations up: how two routers agree to talk, what they say to each other byte by byte, how they notice when the other has gone quiet, and why the routers inside one network need special help to share what they have heard.
Common mix-up: eBGP and iBGP are not two protocols. They use the same messages, the same port and the same state machine. What differs is the rules: a router adds its AS number to the path and usually rewrites the next hop when it talks to another AS (eBGP), and does neither when it talks to a router in its own AS (iBGP). It also refuses to pass routes it learned over iBGP to other iBGP neighbors, and that one rule is the reason the last third of this chapter exists.
If BGP's job as a policy protocol, the one that chooses between paths by business rules rather than by shortest distance, is new to you, start with dynamic routing on networks.jelia.nyc, which introduces it and lets you play with a best-path chooser. This chapter assumes that and looks at the machinery underneath.
The interior protocols in Part II discover their neighbors on their own. An OSPF router multicasts hellos onto every link and makes friends with whoever answers (Chapter 05). BGP does nothing of the kind. Every BGP neighbor, called a peer, is configured by hand: this address, this AS number, these filters. Two networks that connect have usually signed something first, and the configuration is the technical half of that agreement.
Once configured, two peers open an ordinary TCP connection to port 179 and keep it open for as long as the relationship lasts, often for months. That choice, made in the first BGP specification in 1989, shapes everything else about the protocol.
The price of TCP is that a BGP speaker cannot use it to notice a dead peer quickly. TCP can sit for many minutes on a connection whose other end has vanished. So BGP adds a heartbeat of its own, the KEEPALIVE message, and a hold timer that tears the session down if nothing at all arrives for too long.
Both routers are allowed to open the TCP connection, and often both do at once. RFC 4271 §6.8 calls this a connection collision and settles it with a tiebreak: compare the two routers' BGP Identifiers (a 32-bit number, usually one of the router's IPv4 addresses, carried in the OPEN message) and keep the connection that was opened by the router with the higher identifier. The other connection is closed with a NOTIFICATION.
Many operators sidestep this by making one side passive: it listens on port 179 but never dials out. The stepper below works that way. R1 dials, R2 listens.
Because a BGP session is a long-lived TCP connection with a well-known port, it is also something to protect. RFC 7454, the IETF's operational security guidance for BGP, recommends filtering who can reach port 179, authenticating the TCP segments (TCP-AO, or the older TCP MD5 signature option) and, for directly connected peers, the TTL check called GTSM, which only accepts packets that arrive with the maximum TTL and so cannot have come from far away. Chapter 11 covers the routes themselves.
RFC 4271 §8 describes a BGP session as a finite-state machine: a small set of named states, and for each state a list of events that move it to another. A router runs one machine per configured peer. Six states matter.
| State | What the router is doing | How it leaves |
|---|---|---|
| Idle | Nothing. Refuses incoming connections from this peer. | A start event (configuration, or an automatic restart) moves it to Connect, or to Active if the peer is passive. |
| Connect | Trying to open a TCP connection to the peer, with the ConnectRetry timer running. | TCP succeeds: send OPEN, go to OpenSent. TCP fails: go to Active and keep listening. |
| Active | Listening for the peer to connect, waiting for the ConnectRetry timer to try again. | Timer fires: back to Connect for another attempt. Peer connects: send OPEN, go to OpenSent. |
| OpenSent | Has sent its OPEN, waiting for the peer's. Hold timer set to a large value (RFC 4271 suggests 4 minutes). | A valid OPEN arrives: send KEEPALIVE, go to OpenConfirm. An unacceptable one: send NOTIFICATION, go to Idle. |
| OpenConfirm | Both OPENs exchanged and accepted; waiting for the peer's first KEEPALIVE. | KEEPALIVE arrives: Established. Hold timer expires or NOTIFICATION arrives: Idle. |
| Established | The session is up. UPDATEs flow both ways; every message received restarts the hold timer. | Hold timer expires, a NOTIFICATION is sent or received, or TCP drops: Idle, and every route from this peer is withdrawn. |
The confusing name is Active. It sounds like the good state, and it is nearly the opposite: a router in Active has tried to reach its peer, failed, and is waiting to try again. A session that sits in Active, or flips between Connect and Active forever, is a TCP problem, not a BGP one. The usual causes are easy to list and tedious to find.
ebgp-multihop setting is one example).Problems inside BGP look different. If TCP works but the OPEN is wrong, the session gets as far as OpenSent, a NOTIFICATION goes out, and the machine drops back to Idle, then retries a little later and fails the same way. Operators call that a flapping session. The most common OPEN mistake is a wrong AS number: R2 was told to expect R1 in AS 64496 and R1's OPEN says something else, so R2 answers with NOTIFICATION code 2, subcode 2, "Bad Peer AS."
A mismatched hold time, by contrast, is not an error. Each side proposes one in its OPEN, and both use the smaller.
RFC 4271 suggests a hold time of 90 seconds with keepalives every 30. Many implementations default to 180 and 60 instead; the sample show bgp neighbors output in FRRouting's documentation, for example, reports "Hold time is 180, keepalive interval is 60 seconds." Three minutes is a long time to keep sending traffic toward a router that has died, which is why fast detection is usually handed to BFD (Chapter 07) rather than squeezed out of BGP's own timers.
Two routers, R1 in AS 64496 and R2 in AS 64497, each running RFC 4271's state machine for the other. R1 dials out; R2 is passive and only listens. Press Start and watch the messages cross. Then break things: block port 179 and see both sides stall in Active; give R2 a typo in R1's AS number and watch the session flap; set a hold time of 1 or 2 seconds; or make R2 go silent and watch R1's hold timer drain. Timers run faster than real life; the speed chips change by how much.
Everything BGP says is one of five message types, and every message starts with the same 19-byte header. Because TCP delivers a stream of bytes rather than separate packets, the header's main job is to tell the receiver where each message ends.
The marker is sixteen bytes of 0xFF. It once had room for authentication data; RFC 4271 fixed it at all ones, and today it serves as a sanity check that the receiver is still in step with the stream. The length covers the whole message including the header, and must be between 19 and 4,096. The type says which of the five messages follows. The bytes above are a complete KEEPALIVE: a header with nothing after it.
| Type | Message | Carries | Sent when |
|---|---|---|---|
| 1 OPEN | Introduction | Version (4), My AS (2 bytes), Hold Time, BGP Identifier, optional parameters (capabilities) | Once, right after TCP connects |
| 2 UPDATE | News | Withdrawn prefixes, path attributes, and the prefixes (NLRI) those attributes describe | Whenever a route appears, changes or disappears |
| 3 NOTIFICATION | Goodbye | Error code, subcode and data | On any fatal error or shutdown; the connection closes right after |
| 4 KEEPALIVE | Heartbeat | Nothing: header only, 19 bytes | To confirm an OPEN, then often enough to keep the hold timer from expiring |
| 5 ROUTE-REFRESH | "Tell me again" | AFI and SAFI to resend (RFC 2918) | When the receiver changes its import policy and wants the peer's routes re-sent |
OPEN is where the two routers negotiate. Beyond the fixed fields it carries capabilities (RFC 5492): short codes that announce optional features, each used only if both sides advertise it. Capabilities are how BGP has grown for three decades without a version 5. Multiprotocol support, route refresh, four-byte AS numbers, ADD-PATH and larger messages are all switched on this way.
The My AS field is only two bytes wide, because when BGP-4 was written nobody expected more than 65,535 networks. When four-byte AS numbers arrived, RFC 6793 kept the field and added a capability, code 65, carrying the real four-byte number. A router whose number does not fit puts 23456, AS_TRANS, in the old field. Old routers see a valid-looking two-byte AS; new routers read the capability. AS_TRANS appears in AS paths for the same reason: an old router passes on a path in which every unfittable AS has been replaced by 23456, and a separate attribute, AS4_PATH, carries the true numbers past it.
NOTIFICATION is BGP's only way of reporting an error, and it is final: the sender closes the TCP connection right after sending it, and both sides throw away every route learned over the session. The code says which part of the protocol went wrong.
| Code | Meaning | Common subcodes |
|---|---|---|
| 1 | Message Header Error | 1 Connection Not Synchronized (bad marker) · 2 Bad Message Length · 3 Bad Message Type |
| 2 | OPEN Message Error | 1 Unsupported Version · 2 Bad Peer AS · 3 Bad BGP Identifier · 6 Unacceptable Hold Time |
| 3 | UPDATE Message Error | 1 Malformed Attribute List · 2 Unrecognized Well-known Attribute · 3 Missing Well-known Attribute · 6 Invalid ORIGIN |
| 4 | Hold Timer Expired | (none) |
| 5 | Finite State Machine Error | A message arrived that makes no sense in the current state |
| 6 | Cease | Not an error: an operator shut the session, a prefix limit was hit, and so on (subcodes in RFC 4486) |
After the 19-byte header, an OPEN carries: Version (1 byte, always 4), My Autonomous System (2 bytes), Hold Time (2 bytes, seconds), BGP Identifier (4 bytes), Optional Parameters Length (1 byte), then the optional parameters. Each parameter is a type, a length and a value; type 2 holds one or more capabilities, each again a code, a length and a value. The smallest legal OPEN is therefore 29 bytes.
A few capability codes from the IANA registry you will meet often: 1 Multiprotocol Extensions (with an AFI and SAFI), 2 Route Refresh, 5 Extended Next Hop Encoding (RFC 8950), 6 Extended Message (RFC 8654), 64 Graceful Restart (RFC 4724), 65 four-octet AS number, 69 ADD-PATH. The decoder below reads all of them, and its OPEN preset is a router in AS 65551, a documentation four-byte AS, introducing itself with AS_TRANS.
A ROUTE-REFRESH message is just the header, an AFI (2 bytes), a reserved byte and a SAFI. Before RFC 2918 the only way to see a peer's routes again after a policy change was to reset the session, or to keep an unfiltered copy of everything the peer had ever sent ("soft reconfiguration inbound"), which costs memory.
An UPDATE has three parts. First a list of withdrawn routes: prefixes the sender no longer reaches. Then the path attributes: facts about a path, such as the ASes it crosses and where to send the packets. Last the NLRI, network layer reachability information, which is BGP's name for the list of prefixes that share those attributes. One UPDATE can announce hundreds of prefixes at once if they all came by the same path, and that packing is a large part of how BGP stays efficient.
Prefixes are packed tightly: one byte for the prefix length, then only as many address bytes as the length needs. 203.0.113.0/24 takes four bytes on the wire (18 CB 00 71: length 24, then 203.0.113), and 198.51.100.128/25 takes five.
Each attribute starts with a flags byte, a type code and a length. Two flag bits sort attributes into four kinds, and those kinds decide what a router does with an attribute it has never heard of.
| Kind | Flags | Rule | Examples (type code) |
|---|---|---|---|
| Well-known mandatory | 0x40 | Every implementation understands it; every UPDATE that announces routes must carry it | ORIGIN (1), AS_PATH (2), NEXT_HOP (3) |
| Well-known discretionary | 0x40 | Every implementation understands it; it may be left out | LOCAL_PREF (5), ATOMIC_AGGREGATE (6) |
| Optional transitive | 0xC0 | Need not be understood; if not, pass it on unchanged with the Partial bit (0x20) set | COMMUNITIES (8), AGGREGATOR (7), AS4_PATH (17) |
| Optional non-transitive | 0x80 | Need not be understood; if not, drop it quietly | MED (4), ORIGINATOR_ID (9), CLUSTER_LIST (10), MP_REACH_NLRI (14) |
The optional-transitive rule is what made BGP so extensible: a new attribute can cross the internet through routers that are years older than it, as long as the routers at each end understand it. The fourth flag bit, 0x10, means the length field is two bytes instead of one.
Once two routers have both advertised capability 65 they send every AS number in AS_PATH as four bytes. When a new router talks to an old one, it sends AS_PATH with two-byte numbers, writing 23456 wherever a number does not fit, and attaches AS4_PATH (type 17, optional transitive) with the real four-byte numbers. The old router does not understand AS4_PATH but, because it is transitive, passes it on. The next new router merges the two back into one accurate path. The decoder on this page assumes both ends have negotiated four-byte AS numbers, which is the normal case today.
Fill in a route and the builder encodes the exact UPDATE a router would send, with every length computed. The hex is color-banded and numbered by field, and the table underneath decodes it back. Switch to Decode to paste any BGP message in hex, or pick one of the samples, including several broken ones. The decoder checks lengths, flags and values the way a receiver must, and tells you what RFC 4271 and RFC 7606 say a router should do with it.
| # | Offset | Field | Value |
|---|
BGP-4 was written for IPv4: the NLRI field at the end of an UPDATE can only hold IPv4 prefixes, and NEXT_HOP is exactly four bytes. RFC 4760 removed that limit without changing the message format. It added two optional non-transitive attributes. MP_REACH_NLRI (type 14) carries an address family, a next hop of any length and a list of reachable prefixes; MP_UNREACH_NLRI (type 15) carries withdrawals. The family is named by two numbers: the AFI (address family identifier) says what kind of address, and the SAFI (subsequent address family identifier) says what the routes are for.
| AFI / SAFI | Family | Used for |
|---|---|---|
| 1 / 1 | IPv4 unicast | Ordinary internet routes (also possible in the classic NLRI field) |
| 2 / 1 | IPv6 unicast | The IPv6 internet table |
| 1 / 2 | IPv4 multicast | Routes used for multicast reverse-path checks (Chapter 13) |
| 1 / 4 | IPv4 with MPLS labels | Labeled unicast, RFC 8277 |
| 1 / 128 | MPLS-labeled VPN | Customer routes in provider VPNs (Chapter 12) |
| 25 / 70 | L2VPN EVPN | MAC and IP routes for data-center overlays (Chapter 14) |
Each family is switched on separately with capability 1 in the OPEN, so one TCP session can carry IPv4, IPv6 and VPN routes side by side, each with its own table. That is why BGP has become the general-purpose route distribution protocol of large networks: anything that can be written as a prefix with attributes can ride on it. RFC 8950 adds one more combination worth knowing: IPv4 prefixes announced with an IPv6 next hop, so a link or a whole fabric can carry IPv4 traffic without any IPv4 addresses on its interfaces.
An AS with several border routers has a problem. R1 hears a route from a neighbor network; R5, on the other side of the country, needs to know about it too. The interior protocol (OSPF or IS-IS) cannot carry it: those protocols are built for a few thousand internal prefixes, not a million external ones with policy attached. So the routers inside the AS run BGP among themselves. That is iBGP: the same protocol, between routers with the same AS number.
iBGP behaves differently in three ways. A router does not prepend its AS on iBGP, so the AS_PATH is unchanged inside the network. It does not change NEXT_HOP either: R5 learns a route whose next hop is R1's external neighbor, an address R5 can only reach if the interior protocol knows how to get there. Operators fix that by configuring next-hop-self on border routers, which writes the border router's own loopback into NEXT_HOP before passing the route inward. And, the rule that matters most:
The consequence is that every router that learns an external route must tell every other router in the AS itself, directly. Every pair of BGP routers in the AS needs its own session: a full mesh.
There are two ways out, and both bend the rule in a controlled way.
Route reflectors (RFC 4456) are the common one. A few routers are designated reflectors, and the rest are their clients. A reflector is allowed to pass iBGP routes on: a route from a client goes to all other clients and to the reflector's non-client peers; a route from a non-client goes to clients only; a route from eBGP goes to everyone. Clients need nothing special; they just peer with the reflectors instead of with each other. Reflectors are normally deployed in pairs or more, so one failure doesn't cut the clients off.
Bending the rule brings loops back, so reflection adds two optional non-transitive attributes to prevent them. ORIGINATOR_ID records the BGP Identifier of the router that first injected the route into the AS; a router that sees its own identifier there ignores the route. CLUSTER_LIST records every reflection cluster the route has passed through; a reflector that finds its own cluster ID in the list drops the route.
Confederations (RFC 5065) take the other approach. The AS is split into several member ASes, usually numbered from the private range 64512 to 65534, each a full mesh internally, joined by sessions that behave like eBGP. Inside the confederation the AS_PATH records member ASes in special segments, AS_CONFED_SEQUENCE, which are stripped at the edge, so the outside world sees one AS. LOCAL_PREF and NEXT_HOP pass between members unchanged, as if it were all iBGP. Confederations are rarer than reflectors, but they turn up after mergers, when two networks become one AS and their internal structure survives.
A reflector chooses one best path per prefix and reflects only that one. Its clients therefore see fewer paths than they would in a full mesh. That is called path hiding, and it hurts in two ways: a client cannot fail over quickly to a second path it never received, and it cannot spread traffic across several exits. ADD-PATH (RFC 7911) fixes this by letting a speaker send more than one path per prefix, each tagged with a path identifier.
A reflector also chooses from its own position in the network. If it is far from a client, the exit it thinks is closest may not be the closest one for that client. Placing reflectors carefully, or having them compute best paths from the client's point of view, are the usual answers.
Choose how many BGP routers your AS has, and how many of them act as route reflectors in one redundant cluster. The reflectors peer with each other and every client peers with every reflector. Both designs are drawn and counted.
The original rule for a malformed UPDATE was simple: send NOTIFICATION code 3 and close the session. That protects the receiver from bad data but punishes everything else on the session. One wrongly encoded attribute on one route, perhaps set by a router several networks away and passed along by an optional-transitive rule, would tear down a session carrying a million good routes. When the session came back, the same UPDATE arrived again, and the session dropped again. Real incidents of this kind are why RFC 7606, "Revised Error Handling for BGP UPDATE Messages," was written in 2015.
RFC 7606 sorts errors into three responses, from mildest to most severe.
Treat-as-withdraw only works if the receiver can still find the list of prefixes. So RFC 7606 also asks senders to put MP_REACH_NLRI or MP_UNREACH_NLRI first among the attributes, and to put only one kind of route list in each UPDATE. The decoder above applies these rules: try the "bad ORIGIN" sample and then the "lengths lie" one.
The same instinct, avoid resets, runs through several other extensions. ROUTE-REFRESH lets a router re-read a peer's routes without restarting the session. Graceful restart (RFC 4724) lets a router's control plane restart while its neighbors keep forwarding along the old routes for a while. And graceful shutdown (RFC 8326) lets an operator warn neighbors before taking a session down for maintenance: routes are tagged with the well-known community GRACEFUL_SHUTDOWN, 65535:0, and receivers lower their preference, so traffic has already moved to other paths when the session finally closes.
ADD-PATH (RFC 7911, capability 69) puts a four-byte path identifier in front of each prefix in NLRI, so the same prefix can be announced several times with different attributes without one announcement replacing the last. It is the standard fix for route reflector path hiding, and it is what lets a router pre-install a backup BGP path for fast failover.
Extended messages (RFC 8654, capability 6) raise the 4,096-byte limit to 65,535 for every message except OPEN and KEEPALIVE. Attributes such as long lists of large communities, or BGPsec signatures, outgrew the old limit.
An UPDATE with no withdrawn routes, no attributes and no NLRI, just 23 bytes, is not an error. Graceful restart gives it a meaning: End-of-RIB, "that's everything I have." The decoder recognizes it.
| Question | eBGP (between ASes) | iBGP (inside one AS) |
|---|---|---|
| Peers' AS numbers | Different | The same |
| AS_PATH | Sender prepends its own AS | Passed unchanged |
| NEXT_HOP | Normally set to the sender's own address | Unchanged unless next-hop-self is configured |
| LOCAL_PREF | Not sent; discarded if received | Carried, and compared first in path selection |
| Re-advertise what you learned | Yes, subject to policy | Never to another iBGP peer, unless you are a route reflector |
| Typical peering | Directly connected interface addresses | Loopbacks, reached through the IGP |