Skill: vahagn-madatyan/bgp-analysis (version 0.1.1)

--- name: bgp-analysis description: >- BGP protocol analysis with peer state diagnosis, path selection verification, route filtering validation, and convergence assessment. Multi-vendor coverage for Cisco IOS-XE/NX-OS, Juniper JunOS, and Arista EOS with protocol-first diagnostic reasoning. license: Apache-2.0 metadata: safety: read-only author: network-security-skills-suite version: "1.0.0" openclaw: '{"emoji":"🔍","safetyTier":"read-only","requires":{"bins":["ssh"],"env":[]},"tags":["bgp","routing","protocol"],"mcpDependencies":[],"egressEndpoints":[]}' --- # BGP Protocol Analysis Protocol-reasoning-driven analysis skill for BGP peering, path selection, and route propagation. Unlike device health checks that compare metrics against thresholds, BGP analysis requires interpreting protocol state machines, walking the best-path algorithm, and validating policy application across the control plane. Commands are labeled **[Cisco]**, **[JunOS]**, or **[EOS]** where syntax diverges. Unlabeled statements apply to all three vendors. ## When to Use - BGP peer reported down or stuck in a non-Established state - Suspected route leak — prefixes appearing in tables where they should not - Path selection not matching expectations after policy changes - Convergence too slow after planned maintenance or unplanned failover - Post-change verification of BGP configuration (new peers, policy updates, community changes) - Capacity planning for prefix table growth or session scaling - Investigating asymmetric routing caused by inconsistent BGP attributes ## Prerequisites - SSH or console access to the router (read-only privilege sufficient) - BGP process running on the device with at least one configured peer - Knowledge of expected peer topology: which neighbors should be up, expected prefix counts per peer, intended path selection outcomes - Awareness of configured routing policy: route-maps, prefix-lists, community filters, AS-path access lists, and local-preference assignments - For iBGP: understanding of the route-reflector or full-mesh topology ## Procedure Follow this diagnostic flow sequentially. Each step builds on the data from prior steps. The procedure moves from broad inventory to targeted diagnosis. ### Step 1: BGP Session Inventory Collect all peer states and compare against expected topology. **[Cisco]** ``` show bgp ipv4 unicast summary ``` **[JunOS]** ``` show bgp summary ``` **[EOS]** ``` show ip bgp summary ``` Record each neighbor: address, AS number, state, prefixes received, up/down time. Compare against expected topology — every configured peer should appear. A peer missing from output means it was never configured or was removed. Any peer not showing a numeric prefix count is not Established — proceed to Step 2 for that peer. ### Step 2: Peer State Diagnosis For any peer not in Established state, the BGP FSM state reveals the failure domain. This is the core diagnostic reasoning step. **[Cisco]** ``` show bgp ipv4 unicast neighbors [addr] | include state|last reset|error ``` **[JunOS]** ``` show bgp neighbor [addr] | match "State|Last Error|Last State" ``` **[EOS]** ``` show ip bgp neighbors [addr] | include state|last reset|error ``` Interpret the FSM state to isolate the failure: - **Idle** → BGP process not attempting connection. Causes: administratively shut down, no route to peer address, configured remote AS does not match, or maximum-prefix limit hit triggering teardown. - **Connect** → TCP SYN sent, waiting for response. Peer is unreachable at Layer 3 or a firewall is blocking TCP port 179. - **Active** → TCP connection attempt failed, retrying. Same causes as Connect but the router has cycled back. Check: ACLs blocking port 179, peer not configured for this neighbor, peer address unreachable. - **OpenSent** → TCP connected, OPEN message sent, no reply. Remote end accepted TCP but is not sending OPEN — typically remote BGP not configured for this neighbor or remote peer in admin shutdown. - **OpenConfirm** → OPEN received but parameters rejected. Check: AS number mismatch, capability negotiation failure (AFI/SAFI mismatch), hold timer negotiation failure, authentication (MD5/TCP-AO) mismatch. Check "last reset reason" and "last error" fields — they often provide the definitive cause. Reference: `references/state-machine.md` for full FSM detail. ### Step 3: Route Table Analysis For Established peers, verify prefix exchange matches expectations. **[Cisco]** ``` show bgp ipv4 unicast neighbors [addr] routes | include Total ``` **[JunOS]** ``` show route receive-protocol bgp [addr] table summary ``` **[EOS]** ``` show ip bgp neighbors [addr] received-routes | include Total ``` Compare received prefix count against baseline. Significant deviation indicates: - Drop >10% → upstream is withdrawing routes (maintenance, filter change, or failure) - Increase >10% → route leak or new prefixes originated upstream - Zero received → peer is Established but sending no routes (missing export policy on JunOS, or outbound filter on remote blocking everything) Check advertised prefix count similarly — confirm this router is sending the expected number of routes to each peer. ### Step 4: Path Selection Verification When traffic takes an unexpected path, walk the BGP best-path algorithm to identify which attribute is making the selection. **[Cisco]** ``` show bgp ipv4 unicast [prefix] bestpath ``` **[JunOS]** ``` show route [prefix] detail | match "AS path|Local|MED|Weight|preference" ``` **[EOS]** ``` show ip bgp [prefix] detail ``` The best-path algorithm evaluates in this order (first difference wins): 1. **Weight** (Cisco/EOS local, highest wins — JunOS does not use weight) 2. **Local Preference** (highest wins, default 100) 3. **Locally originated** (network/aggregate preferred over learned) 4. **AS Path length** (shortest wins) 5. **Origin** (IGP < EGP < Incomplete) 6. **MED** (lowest wins, compared only within same neighbor AS by default) 7. **eBGP over iBGP** (external preferred) 8. **IGP metric to next-hop** (lowest wins) 9. **Router ID** (lowest wins, tiebreaker) Identify which attribute selects the current best path. If unexpected, check the route-map or policy applying that attribute on ingress. ### Step 5: Route Filtering Validation Verify that route-maps, prefix-lists, and community filters apply as intended. **[Cisco]** ``` show bgp ipv4 unicast neighbors [addr] policy ``` **[JunOS]** ``` show policy [policy-name] | display detail ``` **[EOS]** ``` show route-map [name] ``` For suspected route leaks: examine the RIB for prefixes that should not be present. Check inbound filters on the peer that is the source. Common leak causes: missing or misordered prefix-list entry, regex error in AS-path filter, community match that is too broad. For missing routes: verify the outbound policy on the advertising peer is not filtering the prefix. On JunOS, a peer with no export policy sends nothing by default — this is the most common JunOS-specific omission. ### Step 6: Convergence Assessment Evaluate convergence behavior and route stability. **[Cisco]** ``` show bgp ipv4 unicast dampening dampened-paths ``` **[JunOS]** ``` show route damping suppressed ``` **[EOS]** ``` show ip bgp dampening dampened-paths ``` Check for dampened (suppressed) routes — these indicate persistent flapping. Review the BGP update activity: high update/withdrawal rates indicate churn. After a planned change, measure convergence time from the change event to the last BGP update. Compare against the target convergence window. ## Threshold Tables Operational parameter norms for BGP — these are protocol-level expectations, not device resource thresholds. | Parameter | Cisco Default | JunOS Default | EOS Default | Notes | |-----------|--------------|---------------|-------------|-------| | Hold Timer | 180s | 90s | 180s | Negotiated to lower value | | Keepalive Interval | 60s | 30s | 60s | Hold/3 by convention | | ConnectRetry Timer | 120s | Varies | 120s | Time between TCP attempts | | MRAI (eBGP) | 30s | 0s (immediate) | 30s | Minimum Route Advertisement Interval | | MRAI (iBGP) | 5s | 0s | 5s | Lower than eBGP for faster iBGP convergence | | Default Local Pref | 100 | 100 | 100 | Same across vendors | **Table Size Norms (IPv4 unicast):** | Deployment Type | Expected Prefixes | Warning | Critical | |----------------|-------------------|---------|----------| | Internet edge (full table) | ~950K | >1M | >1.1M | | Internet edge (partial) | 5K–100K | Varies | Per design | | Enterprise WAN | 100–10K | >2x baseline | >5x baseline | | Data center leaf | 50–5K | >2x baseline | >5x baseline | **Convergence Targets:** | Scenario | Target | Acceptable | Degraded | |----------|--------|------------|----------| | eBGP failover | < 90s | 90–180s | > 180s | | iBGP reconvergence | < 30s | 30–60s | > 60s | | Full table reload | < 5min | 5–10min | > 10min | ## Decision Trees ### Peer Not Established ``` Peer not in Established state ├── State: Idle │ ├── Admin shut? → Check config for "neighbor shutdown" / "deactivate" │ ├── No route to peer? → Check IGP/static route to peer address │ ├── Prefix-limit exceeded? → Check logs for max-prefix teardown │ └── AS mismatch? → Verify "remote-as" matches peer's local AS │ ├── State: Connect / Active │ ├── Peer reachable? → Ping/traceroute peer address │ │ ├── No → Fix Layer 3 reachability (IGP, static route) │ │ └── Yes → TCP port 179 blocked? │ │ ├── ACL on local device? → Check interface/control-plane ACL │ │ ├── ACL on remote device? → Check remote inbound ACL │ │ └── Firewall between peers? → Verify TCP/179 permitted both directions │ └── Peer configured? → Verify remote has this router as neighbor │ ├── State: OpenSent │ ├── Remote not sending OPEN → Peer may not be configured for this neighbor │ ├── TCP resets after connect → Check for TTL issues (eBGP multihop) │ └── Authentication? → Verify MD5/TCP-AO passwords match both sides │ ├── State: OpenConfirm │ ├── Capability mismatch? → Check AFI/SAFI (IPv4/IPv6/VPNv4) match │ ├── AS mismatch in OPEN? → Verify configured AS matches OPEN AS │ ├── Hold timer = 0 on one side? → Both peers must agree or both use 0 │ └── Check NOTIFICATION message → Decode error code/subcode │ └── Established but dropping ├── Hold timer expiry? → Keepalives not arriving (CPU, QoS, path issue) ├── NOTIFICATION received? → Decode error code for root cause ├── Route refresh storm? → Peer sending excessive route-refresh requests └── Max-prefix limit? → Peer sending more prefixes than limit allows ``` ### Unexpected Route Selection ``` Wrong path selected for prefix ├── Check Weight (Cisco/EOS only) │ └── Weight set via route-map? → Highest weight wins ├── Check Local Preference │ └── Local pref differs? → Set via inbound route-map; highest wins ├── Check AS Path Length │ ├── AS prepending applied? → Verify prepend count │ └── AS path differs? → Shortest wins ├── Check Origin │ └── IGP vs Incomplete? → IGP (network statement) preferred ├── Check MED │ ├── MED comparison enabled across AS? → "always-compare-med" │ └── MED set correctly? → Lowest wins within same neighbor AS ├── Check eBGP vs iBGP │ └── External path preferred over internal if equal above ├── Check IGP metric to next-hop │ └── Closest exit wins (hot-potato routing) └── All equal → Lowest Router ID wins (or oldest route if stable) ``` ## Report Template ``` BGP ANALYSIS REPORT ==================== Device: [hostname] Vendor: [Cisco | JunOS | EOS] Check Time: [timestamp] Performed By: [operator/agent] SESSION STATUS: - Total configured peers: [n] - Established: [n] | Not Established: [n] - Peers requiring attention: [list with FSM states] FINDINGS: 1. [Severity] [Category] — [Description] Peer: [neighbor address / AS] Observed: [state or metric] Expected: [normal state or value] Root Cause: [diagnosis from decision tree] Action: [recommended remediation] PATH ANALYSIS: - Prefix: [prefix under review] - Selected path via: [next-hop / AS path] - Selecting attribute: [which best-path attribute decided] - Expected path: [if different, what was expected and why] ROUTE TABLE SUMMARY: - IPv4 prefixes received: [total across all peers] - Baseline deviation: [% change from expected] RECOMMENDATIONS: - [Prioritized action list] NEXT CHECK: [based on severity — CRITICAL: 4hr, WARNING: 24hr, HEALTHY: 7d] ``` ## Troubleshooting ### Session Flapping Peer cycles between Established and Idle/Active repeatedly. Common causes: unstable underlying transport (IGP flap, link errors), aggressive hold timers on congested control planes, or MTU issues on the path causing fragmented keepalives to be dropped. Check `last reset reason` and correlate with interface or IGP events at the same timestamps. ### Route Oscillation The same prefix alternates between two or more paths. Caused by inconsistent MED comparison across route reflectors, or deterministic-MED not enabled when multiple exit points exist to the same neighbor AS. Enable `always-compare-med` and `deterministic-med` to stabilize. ### Memory Pressure from Full Table Full Internet table (~950K IPv4 prefixes) requires 1–2 GB of RIB memory depending on path diversity. Symptoms: slow convergence, peer resets during table reload. Mitigate with soft-reconfiguration inbound (trades memory for stability) or ORF (Outbound Route Filtering) to reduce inbound load. ### Community Stripping Routes arrive without expected communities. Check each transit AS in the path — many providers strip non-standard communities by default. Use large communities (RFC 8092) for end-to-end propagation across providers that strip standard communities. ### JunOS Default Export Policy JunOS sends no routes to a peer without an explicit export policy. If a peer shows Established with zero prefixes sent, add an export policy. This is the most common JunOS-specific BGP issue and does not occur on Cisco or EOS.

path: references/cli-reference.md
content: # BGP CLI Reference — Cisco / JunOS / EOS

Commands organized by diagnostic category with all three vendors side by side.
All commands are read-only (show/display only). Cisco commands validated against
IOS-XE 17.3+ and NX-OS 10.2+; JunOS 21.x+; EOS 4.28+.

## Session Management

| Function | Cisco | JunOS | EOS |
|----------|-------|-------|-----|
| Peer summary | `show bgp ipv4 unicast summary` | `show bgp summary` | `show ip bgp summary` |
| Peer detail | `show bgp ipv4 unicast neighbors [addr]` | `show bgp neighbor [addr]` | `show ip bgp neighbors [addr]` |
| Peer state/errors | `show bgp ipv4 unicast neighbors [addr] \| include state\|error\|reset` | `show bgp neighbor [addr] \| match "State\|Error"` | `show ip bgp neighbors [addr] \| include state\|error\|reset` |
| Peer timers | `show bgp ipv4 unicast neighbors [addr] \| include timer\|hold\|keepalive` | `show bgp neighbor [addr] \| match "Holdtime\|Keepalive"` | `show ip bgp neighbors [addr] \| include timer\|hold\|keepalive` |
| Peer capabilities | `show bgp ipv4 unicast neighbors [addr] \| section capabilities` | `show bgp neighbor [addr] \| find "Options"` | `show ip bgp neighbors [addr] \| section capabilities` |
| Message statistics | `show bgp ipv4 unicast neighbors [addr] \| section Message` | `show bgp neighbor [addr] \| match "Messages"` | `show ip bgp neighbors [addr] \| section Message` |

### Session Notes

- **Cisco (IOS-XE vs NX-OS):** IOS-XE uses `show bgp ipv4 unicast`; older IOS uses `show ip bgp`. NX-OS uses `show bgp ipv4 unicast` as well but some outputs differ in format.
- **JunOS:** The `show bgp summary` output lists peer state as text ("Established", "Active", etc.). Prefix count appears as "Accepted" prefixes.
- **EOS:** Output format closely resembles Cisco IOS-XE. `show ip bgp summary` works identically to Cisco's `show ip bgp summary`.

## Route Table

| Function | Cisco | JunOS | EOS |
|----------|-------|-------|-----|
| Full BGP table | `show bgp ipv4 unicast` | `show route protocol bgp` | `show ip bgp` |
| Specific prefix | `show bgp ipv4 unicast [prefix]` | `show route [prefix] detail` | `show ip bgp [prefix]` |
| Best path detail | `show bgp ipv4 unicast [prefix] bestpath` | `show route [prefix] detail` | `show ip bgp [prefix] detail` |
| Received routes (per peer) | `show bgp ipv4 unicast neighbors [addr] routes` | `show route receive-protocol bgp [addr]` | `show ip bgp neighbors [addr] received-routes` |
| Advertised routes (per peer) | `show bgp ipv4 unicast neighbors [addr] advertised-routes` | `show route advertising-protocol bgp [addr]` | `show ip bgp neighbors [addr] advertised-routes` |
| Route count summary | `show bgp ipv4 unicast summary` (PfxRcd column) | `show bgp summary` (Accepted column) | `show ip bgp summary` (PfxRcd column) |
| RIB failure | `show bgp ipv4 unicast rib-failure` | `show route resolution unresolved` | `show ip bgp rib-failure` |

### Route Table Notes

- **Cisco:** `PfxRcd` column in summary shows received prefix count; a text state name means the peer is not Established.
- **JunOS:** `show route protocol bgp` shows active routes only. Use `show route protocol bgp all` to see hidden/rejected routes. The `receive-protocol`/`advertising-protocol` syntax is unique to JunOS.
- **EOS:** Supports `received-routes` only when `soft-reconfiguration inbound` is enabled or with `neighbor [addr] route-reflector-client`.

## Path Attributes

| Function | Cisco | JunOS | EOS |
|----------|-------|-------|-----|
| AS path | `show bgp ipv4 unicast [prefix]` (AS Path line) | `show route [prefix] detail \| match "AS path"` | `show ip bgp [prefix]` (AS Path line) |
| Local preference | `show bgp ipv4 unicast [prefix]` (localpref line) | `show route [prefix] detail \| match "Local"` | `show ip bgp [prefix]` (localpref line) |
| MED | `show bgp ipv4 unicast [prefix]` (metric line) | `show route [prefix] detail \| match "MED"` | `show ip bgp [prefix]` (metric line) |
| Weight | `show bgp ipv4 unicast [prefix]` (weight line) | *(not applicable — JunOS has no weight)* | `show ip bgp [prefix]` (weight line) |
| Communities | `show bgp ipv4 unicast [prefix]` (community line) | `show route [prefix] detail \| match "Communities"` | `show ip bgp [prefix]` (community line) |
| Extended communities | `show bgp ipv4 unicast [prefix]` | `show route [prefix] extensive` | `show ip bgp [prefix] detail` |
| Origin | `show bgp ipv4 unicast [prefix]` (Origin line) | `show route [prefix] detail` | `show ip bgp [prefix]` (Origin line) |
| Next-hop | `show bgp ipv4 unicast [prefix]` (next hop line) | `show route [prefix] detail \| match "Next hop"` | `show ip bgp [prefix]` (next hop line) |

### Path Attribute Notes

- **Weight:** Cisco and EOS support weight (local to the router, not propagated). JunOS does not implement weight — use local-preference or policy instead.
- **MED comparison:** By default, MED is compared only between paths from the same neighbor AS. Enable `always-compare-med` (Cisco/EOS) or set `path-selection med-always` (JunOS) for cross-AS comparison.
- **Communities:** All three vendors display standard communities. Large communities (RFC 8092) require explicit configuration to display in output.

## Filtering and Policy

| Function | Cisco | JunOS | EOS |
|----------|-------|-------|-----|
| Applied route-map | `show bgp ipv4 unicast neighbors [addr] policy` | `show bgp neighbor [addr] \| match "Import\|Export"` | `show ip bgp neighbors [addr] \| include route-map` |
| Route-map detail | `show route-map [name]` | `show policy [name]` | `show route-map [name]` |
| Prefix-list | `show ip prefix-list [name]` | `show policy-options prefix-list [name]` | `show ip prefix-list [name]` |
| AS-path filter | `show ip as-path-access-list [name]` | `show policy-options as-path [name]` | `show ip as-path-access-list [name]` |
| Community list | `show ip community-list [name]` | `show policy-options community [name]` | `show ip community-list [name]` |
| Filtered routes (inbound) | `show bgp ipv4 unicast neighbors [addr] received-routes` vs `routes` | `show route receive-protocol bgp [addr] all` (includes rejected) | `show ip bgp neighbors [addr] received-routes` (if soft-reconfig enabled) |

### Filtering Notes

- **JunOS default export policy:** JunOS requires an explicit export policy to advertise routes to a BGP peer. Without one, zero prefixes are sent. This is the most common vendor-specific gotcha.
- **Cisco/EOS:** Without an outbound route-map, all routes in the BGP table are advertised (subject to next-hop reachability).
- **Comparing filtered vs unfiltered:** On Cisco/EOS, compare `received-routes` (pre-filter) vs `routes` (post-filter). On JunOS, use `all` flag to see rejected routes alongside accepted ones.

## Convergence and Dampening

| Function | Cisco | JunOS | EOS |
|----------|-------|-------|-----|
| Dampened routes | `show bgp ipv4 unicast dampening dampened-paths` | `show route damping suppressed` | `show ip bgp dampening dampened-paths` |
| Dampening params | `show bgp ipv4 unicast dampening parameters` | `show route damping parameters` | `show ip bgp dampening parameters` |
| Flap statistics | `show bgp ipv4 unicast dampening flap-statistics` | `show route damping history` | `show ip bgp dampening flap-statistics` |
| Update activity | `show bgp ipv4 unicast update-group` | `show bgp group [name] detail` | `show ip bgp update-group` |
| Soft reset (inbound) | `show bgp ipv4 unicast neighbors [addr] received-routes` | `show route receive-protocol bgp [addr]` | `show ip bgp neighbors [addr] received-routes` |
| BGP event log | `show bgp ipv4 unicast neighbors [addr] \| section Event` | `show log messages \| match bgp` | `show ip bgp neighbors [addr] \| section Event` |

### Convergence Notes

- **MRAI (Minimum Route Advertisement Interval):** Cisco and EOS default to 30s for eBGP, 5s for iBGP. JunOS defaults to 0s (immediate advertisements). Lower MRAI = faster convergence but more update churn.
- **Dampening:** Enabled per address-family. Default parameters differ across vendors. Check if dampening is even configured before investigating suppressed routes.
- **Update groups:** Cisco and EOS group peers with identical outbound policy into update groups for efficiency. JunOS uses BGP groups similarly. Update-group churn indicates frequent policy changes.


path: references/state-machine.md
content: # BGP Finite State Machine (FSM)

Reference for the BGP connection state machine as defined in RFC 4271. Each
state describes what the BGP speaker is doing, what events cause transitions,
and what commonly causes a peer to become stuck in that state.

## State Overview

```
Idle ──→ Connect ──→ Active ──→ OpenSent ──→ OpenConfirm ──→ Established
  ↑          │          │           │              │               │
  └──────────┴──────────┴───────────┴──────────────┴───────────────┘
                    (any error or NOTIFICATION → back to Idle)
```

The FSM is per-peer. Each configured neighbor has an independent FSM instance.

## States

### Idle

**Definition:** Initial state. The BGP process is not attempting to connect to
the peer. No TCP session exists.

**Entry conditions:**
- BGP process just started or peer just configured
- Previous session terminated (NOTIFICATION sent/received, TCP failure, admin reset)
- Manual reset (`clear bgp neighbor`)

**Exit transitions:**
- **Start event** → transition to **Connect** (start ConnectRetry timer, initiate TCP)
- **Automatic start** → same as Start event (triggered by ConnectRetry expiry if auto-start enabled)

**Stuck-state causes:**
- Peer is administratively shut down (`neighbor shutdown` / `deactivate`)
- No route to peer address in the routing table (IGP/static missing)
- Maximum-prefix limit was exceeded, causing the session to be torn down with a penalty delay
- Peer's configured remote-AS does not match — BGP process refuses to initiate
- Passive mode configured — waiting for remote side to initiate

**Diagnostic commands:**
- Check for admin shutdown in configuration
- Verify route to peer exists: `show ip route [peer-addr]` / `show route [peer-addr]`
- Check logs for max-prefix or NOTIFICATION messages

### Connect

**Definition:** TCP SYN has been sent to the peer on port 179. Waiting for TCP
three-way handshake to complete.

**Entry conditions:**
- Transition from Idle via Start event
- ConnectRetry timer expired in Active state, triggering a new TCP attempt

**Exit transitions:**
- **TCP connection succeeds** → transition to **OpenSent** (send OPEN, start Hold timer)
- **TCP connection fails** → transition to **Active** (restart ConnectRetry timer)
- **ConnectRetry expires** → stay in **Connect** (retry TCP connection)

**Stuck-state causes:**
- Peer address unreachable at Layer 3 (no route, interface down)
- Firewall blocking TCP port 179 between peers
- ACL on local or remote device rejecting TCP/179
- TCP SYN lost due to congestion or MTU issues
- Peer not listening on port 179 (BGP not running on remote)

**Timer:** ConnectRetry (default 120s Cisco/EOS, varies JunOS)

### Active

**Definition:** TCP connection attempt failed. The router is listening for an
incoming TCP connection from the peer while also periodically retrying outbound
TCP connections.

**Entry conditions:**
- TCP connection failed in Connect state
- ConnectRetry timer expired in Active (cycle back)

**Exit transitions:**
- **TCP connection succeeds (inbound or outbound)** → transition to **OpenSent**
- **ConnectRetry expires** → transition to **Connect** (try again)

**Stuck-state causes:**
- Same as Connect — peer unreachable, port 179 blocked, ACL filtering
- Additionally: eBGP multihop not configured when peers are not directly connected
  (TTL=1 causes TCP to fail on multi-hop paths)
- MD5/TCP-AO authentication mismatch — TCP handshake silently fails because
  the TCP segment authentication check fails before the session establishes
- Dual-stack confusion — peer configured with IPv6 address but only IPv4
  reachability exists (or vice versa)

**Note:** Active does NOT mean the session is actively working. It means the
router is actively trying to establish a TCP connection. This is the most
commonly misunderstood BGP state name.

### OpenSent

**Definition:** TCP connection established. The local router has sent its BGP
OPEN message and is waiting for the peer's OPEN message in return.

**Entry conditions:**
- TCP connection succeeded from Connect or Active state
- OPEN message sent to peer
- Hold timer set to 4 minutes (large value, pre-negotiation)

**Exit transitions:**
- **OPEN received (valid)** → transition to **OpenConfirm** (send KEEPALIVE, negotiate hold timer)
- **OPEN received (error)** → send **NOTIFICATION** → transition to **Idle**
- **TCP connection fails** → transition to **Active**
- **Hold timer expires** → send NOTIFICATION → transition to **Idle**

**Stuck-state causes:**
- Remote BGP process accepted TCP but does not have this router configured as
  a neighbor — it accepts TCP on 179 but does not send OPEN
- Remote peer is in admin shutdown — TCP connects (RST not sent) but no OPEN
- eBGP TTL security: TCP succeeded but OPEN packets dropped due to GTSM
  (Generalized TTL Security Mechanism) — TTL check applied at BGP layer
- Version mismatch (extremely rare with modern equipment)

### OpenConfirm

**Definition:** OPEN message received from peer. Parameters are being validated.
A KEEPALIVE has been sent. Waiting for a KEEPALIVE from the peer to confirm.

**Entry conditions:**
- Valid OPEN received in OpenSent state
- Hold timer renegotiated to the lower of the two peers' configured values
- KEEPALIVE sent to peer

**Exit transitions:**
- **KEEPALIVE received** → transition to **Established**
- **NOTIFICATION received** → transition to **Idle**
- **Hold timer expires** → send NOTIFICATION → transition to **Idle**
- **TCP connection fails** → transition to **Idle**

**Stuck-state causes:**
- Capability mismatch: peers disagree on AFI/SAFI (e.g., one side configured
  for IPv4 unicast only, other expects VPNv4). The OPEN is accepted but the
  capability negotiation triggers a NOTIFICATION.
- Hold timer negotiation failure: one peer has hold=0 (no keepalives) and the
  other has a non-zero hold timer — this is a valid configuration but can cause
  issues if not matched
- AS number in OPEN does not match configured remote-AS — NOTIFICATION sent with
  "Bad Peer AS" error code
- Router ID collision — two peers with the same Router ID

### Established

**Definition:** BGP session is fully operational. UPDATE, KEEPALIVE, NOTIFICATION,
and ROUTE-REFRESH messages are exchanged. Routes are being learned and advertised.

**Entry conditions:**
- KEEPALIVE received in OpenConfirm state
- Hold timer reset
- BGP route exchange begins (UPDATE messages)

**Exit transitions:**
- **NOTIFICATION sent or received** → transition to **Idle** (session teardown)
- **Hold timer expires** → send NOTIFICATION (Hold Timer Expired) → **Idle**
- **TCP connection fails** → transition to **Idle**
- **Admin reset** (`clear bgp neighbor`) → send NOTIFICATION (Cease) → **Idle**

**Causes of leaving Established:**
- Hold timer expiry: keepalives not arriving due to CPU overload, QoS dropping
  BGP packets, or path failure that does not kill TCP immediately
- NOTIFICATION received: peer detected an error (malformed UPDATE, malformed
  attribute, finite state machine error)
- Maximum-prefix limit exceeded: too many prefixes received from peer
- Route-refresh storm: excessive route-refresh requests causing CPU overload
- Admin action: manual clear, configuration change requiring session reset,
  maintenance shutdown

## Timer Reference

| Timer | Purpose | Default | Behavior |
|-------|---------|---------|----------|
| ConnectRetry | Interval between TCP connection attempts | 120s (Cisco/EOS), varies (JunOS) | Starts in Idle, resets on each transition |
| Hold Timer | Maximum interval between messages | 180s (Cisco/EOS), 90s (JunOS) | Negotiated to lower of two peers; reset on KEEPALIVE/UPDATE |
| Keepalive | Interval between KEEPALIVE messages | Hold/3 (60s Cisco/EOS, 30s JunOS) | Only active in Established |
| MinASOriginationInterval | Delay before originating new routes | 15s | Dampens local origination |
| MRAI | Minimum Route Advertisement Interval | 30s eBGP, 5s iBGP (Cisco/EOS); 0s (JunOS) | Paces UPDATE messages to peers |

## Event-Driven Transitions Summary

| Event | In State | Transition To | Action |
|-------|----------|---------------|--------|
| Admin Start | Idle | Connect | Initiate TCP, start ConnectRetry |
| TCP connect success | Connect/Active | OpenSent | Send OPEN, start Hold timer |
| TCP connect fail | Connect | Active | Restart ConnectRetry |
| OPEN received (valid) | OpenSent | OpenConfirm | Send KEEPALIVE, negotiate Hold |
| OPEN received (error) | OpenSent | Idle | Send NOTIFICATION |
| KEEPALIVE received | OpenConfirm | Established | Start route exchange |
| NOTIFICATION received | Any | Idle | Close TCP, log reason |
| Hold timer expired | OpenSent/OpenConfirm/Established | Idle | Send NOTIFICATION |
| TCP connection lost | Any (except Idle) | Idle | Log, restart ConnectRetry |
| Admin Stop | Any | Idle | Send NOTIFICATION (Cease), close TCP |

## NOTIFICATION Error Codes

Common error codes seen when sessions fail:

| Code | Subcode | Meaning | Common Cause |
|------|---------|---------|--------------|
| 1 | 1 | Message Header — Connection Not Synchronized | Corrupted TCP stream |
| 2 | 2 | OPEN — Bad Peer AS | Remote AS does not match configured remote-as |
| 2 | 4 | OPEN — Unsupported Optional Parameter | Capability negotiation failure |
| 2 | 6 | OPEN — Unacceptable Hold Time | Hold timer value rejected |
| 3 | 1 | UPDATE — Malformed Attribute List | Corrupted or unexpected attribute |
| 3 | 5 | UPDATE — Attribute Length Error | Attribute exceeds expected length |
| 4 | 0 | Hold Timer Expired | No KEEPALIVE received within hold time |
| 5 | 0 | FSM Error | Unexpected event for current state |
| 6 | 1 | Cease — Maximum Prefix Limit | Peer exceeded prefix-limit |
| 6 | 2 | Cease — Administrative Shutdown | Peer sent shutdown (with optional message) |
| 6 | 4 | Cease — Administrative Reset | Manual clear/reset on peer side |
| 6 | 6 | Cease — Other Configuration Change | Peer reconfigured requiring reset |


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)