BGP Deep Dive Part 3: The UPDATE Message and the Attribute Catalog That Is BGP's Actual Policy Engine
This is the post Part 1 was building toward. Everything about BGP being a policy negotiation rather than a shortest-path computation comes down to what actually rides in the UPDATE message, and specifically to the path attributes attached to every route. Doyle’s NLRI chapter is where this clicked for me, not because path attributes are complicated individually, most of them aren’t, but because seeing them laid out as a full catalog is what makes it obvious they’re not incidental metadata. They’re the mechanism. Policy isn’t something layered on top of BGP; policy is what these fields, read and acted on by every speaker along the path, actually are.
What an UPDATE message actually contains
An UPDATE message carries three things, in this order: a list of routes being withdrawn, a set of path attributes, and a list of reachable destinations, NLRI, Network Layer Reachability Information, that those attributes apply to.
- Withdrawn Routes: a length field followed by a list of prefixes that are no longer reachable. A single UPDATE can be pure withdrawal, pure advertisement, or both.
- Path Attributes: a length field followed by the attributes themselves, each one a type, a set of flags, a length, and a value. Every attribute in this section applies to every prefix listed in the NLRI field of the same message, which is exactly why BGP implementations group prefixes that share identical attributes into the same UPDATE where they can, rather than sending one message per prefix.
- NLRI: the actual prefixes, expressed as length-prefixed values, being advertised with the attributes above.
That structure, attributes described once and applied to a batch of prefixes, is itself worth pausing on: it’s an efficiency mechanism, but it also means a single UPDATE is answering one specific question, what policy applies to reaching this exact set of destinations, for however many prefixes happen to share that answer.
Four categories, and what they mean operationally
RFC 4271 splits every path attribute into one of four categories, and the category isn’t just classification, it’s a set of rules about who has to understand the attribute and what happens if they don’t:
- Well-known mandatory: every BGP implementation must recognize it, and it must be present in every UPDATE that carries NLRI. Missing one of these is a protocol error serious enough to tear the session down with a NOTIFICATION.
- Well-known discretionary: every implementation must recognize it, but it isn’t required in every UPDATE.
- Optional transitive: not every implementation needs to understand it, but if a speaker doesn’t recognize one, it’s still required to pass it along unchanged to the next AS, with a Partial bit set to flag that it went through unrecognized. This is the category that lets new attributes get deployed incrementally across a network that doesn’t upgrade all at once.
- Optional non-transitive: a speaker that doesn’t recognize one of these is required to quietly drop it rather than forward it. These attributes are meant to have local, hop-by-hop significance, not to survive the trip across multiple ASes.
Three attributes, ORIGIN, AS_PATH, and NEXT_HOP, are well-known mandatory in both eBGP and iBGP contexts. Everything else on the list below is either well-known discretionary or optional, which is a distinction worth holding onto going into the rest of this series: the mandatory three are what makes a route a route, everything past that is where policy actually happens.
ORIGIN
ORIGIN is well-known mandatory and set once, by whichever speaker originates the route, IGP if the prefix came from that AS’s own interior routing, EGP if it arrived via the old exterior protocol (a historical value that’s essentially never seen on a modern network), or INCOMPLETE if it was learned some other way, most commonly via redistribution from a source BGP itself can’t vouch for. It’s not meant to change as the route propagates, and it feeds into the best path algorithm covered in Part 4 as one of the later, lower-priority tiebreakers.
AS_PATH
AS_PATH is well-known mandatory and does two jobs at once, as covered in Part 1: loop prevention and a policy input. The mechanics matter more than they first appear. When a speaker advertises a route to an external peer, it prepends its own AS number onto the front of the AS_PATH; when it advertises the same route to an internal peer, it doesn’t touch the AS_PATH at all, iBGP sessions carry an AS_PATH that only ever changes at the AS’s edges. A route originated internally gets sent to external peers with a brand new AS_PATH containing just the originating AS, and to internal peers with an empty AS_PATH entirely.
The list itself is built from segments, most commonly AS_SEQUENCE, an ordered list reflecting the actual path taken. Route aggregation can introduce the other segment type, AS_SET, an unordered set representing the ASes an aggregate route was formed from without preserving a specific order, which is what makes ATOMIC_AGGREGATE, below, necessary as a signal when that set gets dropped for a cleaner-looking route.
NEXT_HOP, and the gotcha that catches almost everyone once
NEXT_HOP is well-known mandatory, and its rules are the most involved of the three mandatory attributes because they change depending on who’s being advertised to. The short version: when a route is advertised across an eBGP session, the advertising speaker typically sets NEXT_HOP to its own address on the link to that peer. When the same route is advertised across an iBGP session, the default behavior is different, and this is where the gotcha lives: an iBGP speaker propagating a route learned from an external peer normally does not rewrite NEXT_HOP to itself. The NEXT_HOP stays pointing at the original eBGP-facing router’s external-facing address, even as the route is passed along, unmodified, to iBGP peers that may have no idea how to reach that address at all.
This is the single most common “the route is in the table but traffic won’t forward” issue in BGP, and it’s exactly the kind of thing Doyle’s spot-the-issue framing trains you to catch: a route can be present, best-path selected, and installed, and still be completely unusable because the router can’t resolve NEXT_HOP to an actual outbound interface via its IGP. The fix, setting next-hop-self on the iBGP-facing router that learned the route externally, is one of the first things anyone configuring iBGP learns, but it’s worth understanding as a direct, mechanical consequence of RFC 4271’s NEXT_HOP propagation rule rather than a piece of received wisdom to apply without knowing why.
MULTI_EXIT_DISC (MED)
MED is optional non-transitive and discretionary, a four-octet metric meant to let a neighboring AS express a preference among multiple entry points into it. All else equal, a lower MED is preferred. The non-transitive category matters here specifically: a MED received from a neighboring AS must not be passed on to a third AS, it’s meant to influence exactly one adjacent AS’s decision, not to propagate as if it were a global metric. MED is also one of the more misused attributes in practice, since comparing MED values from routes learned via different neighboring ASes is explicitly not meaningful, a rule Part 4’s best path algorithm enforces directly.
LOCAL_PREF
LOCAL_PREF is technically classified as well-known discretionary, but RFC 4271’s actual language is stronger than that label suggests: it “SHALL be included in all UPDATE messages that a given BGP speaker sends to other internal peers.” In practice, LOCAL_PREF is the closest thing BGP has to a single dial for which of these routes a given AS wants to prefer, set by local policy on ingress and higher values preferred. It’s also strictly an iBGP attribute: a speaker must not send it to external peers at all, except under BGP confederations, which get their own treatment in Part 6.
ATOMIC_AGGREGATE and AGGREGATOR
These two exist as a pair. When a speaker aggregates several more-specific routes into one summary, and the aggregate’s AS_PATH drops some of the AS numbers that were actually present in the routes being summarized (by using an AS_SET rather than preserving every hop), it should attach ATOMIC_AGGREGATE to flag that the AS_PATH no longer fully represents the real path, the route is still loop-free, but the path information has been deliberately made less precise. AGGREGATOR, optional transitive, is the accompanying attribute that identifies which AS and which router actually performed the aggregation, carrying that speaker’s AS number and IP address. Neither attribute is common in a typical enterprise network, but both show up constantly in internet-scale routing, where aggregation is routine.
What’s covered later, not skipped
A handful of attributes are deliberately not covered in depth here because they belong to topics with their own posts: COMMUNITY, the general-purpose tagging mechanism, gets Part 7 to itself. ORIGINATOR_ID and CLUSTER_LIST, both optional non-transitive attributes that only exist because of route reflection, belong in Part 6 alongside the route reflector architecture that requires them. MP_REACH_NLRI and MP_UNREACH_NLRI, the attributes that let a single BGP session carry address families beyond plain IPv4 unicast, are the entire subject of Part 10.
That’s a deliberate structural choice worth naming: the attribute catalog in this post is complete for what a purely IPv4-unicast, non-reflected, non-confederated BGP session needs. Everything else in this series is really just more attributes, and more rules about how existing ones behave, layered on top of exactly the foundation this post just laid out.
Part 4 puts this catalog to work: the full best path selection algorithm, attribute by attribute, in the actual order a BGP speaker evaluates them.