Skip to main content

rustbgpd_wire/
lib.rs

1//! rustbgpd-wire — BGP message codec
2//!
3//! Pure codec library for BGP message encoding and decoding.
4//! Zero internal dependencies. This crate is independently publishable.
5//!
6//! # Message Types
7//!
8//! - [`OpenMessage`] — BGP OPEN with capability negotiation
9//! - [`NotificationMessage`] — BGP NOTIFICATION with error codes
10//! - [`UpdateMessage`] — BGP UPDATE (wire-level framing, raw bytes in M0)
11//! - `Keepalive` — represented as [`Message::Keepalive`] unit variant
12//!
13//! # Entry Points
14//!
15//! - [`decode_message`] — decode a complete BGP message from bytes
16//! - [`encode_message`] — encode a BGP message to bytes
17//! - [`peek_message_length`] — check if a
18//!   complete message is available (for transport framing)
19//!
20//! # Invariants
21//!
22//! - Maximum message size: 4096 bytes (RFC 4271 §4.1)
23//! - No panics on malformed input — all paths return `Result`
24//! - No `unsafe` code
25
26#![deny(unsafe_code)]
27#![deny(clippy::all)]
28#![warn(clippy::pedantic)]
29
30/// Path attribute types and codec (`ORIGIN`, `AS_PATH`, `NEXT_HOP`, etc.).
31pub mod attribute;
32/// BGP capability negotiation types and codec (RFC 5492).
33pub mod capability;
34/// Wire-format constants: markers, lengths, type codes.
35pub mod constants;
36/// Decode and encode error types.
37pub mod error;
38/// EVPN NLRI types and codec (RFC 7432 + RFC 9136).
39pub mod evpn;
40/// FlowSpec NLRI types and codec (RFC 8955 / RFC 8956).
41pub mod flowspec;
42/// BGP message header codec (RFC 4271 §4.1).
43pub mod header;
44/// KEEPALIVE message encoding and validation.
45pub mod keepalive;
46/// Top-level BGP message enum and codec dispatch.
47pub mod message;
48/// NLRI prefix types and codec (IPv4, IPv6, Add-Path).
49pub mod nlri;
50/// NOTIFICATION error codes, subcodes, and shutdown communication.
51pub mod notification;
52/// NOTIFICATION message struct and codec.
53pub mod notification_msg;
54/// OPEN message struct and codec.
55pub mod open;
56/// Outbound Route Filtering (ORF) types and codec (RFC 5291 + RFC 5292).
57pub mod orf;
58/// PMSI Tunnel path attribute (RFC 6514 §5) — used by EVPN Type 3 IMET
59/// for ingress-replication BUM.
60pub mod pmsi;
61/// ROUTE-REFRESH message struct and codec (RFC 2918 / RFC 7313).
62pub mod route_refresh;
63/// UPDATE message struct, codec, and builder.
64pub mod update;
65/// UPDATE attribute semantic validation (RFC 4271 §6.3).
66pub mod validate;
67
68// Re-export primary public API
69pub use capability::{
70    AddPathFamily, AddPathMode, Afi, BgpRole, Capability, ExtendedNextHopFamily,
71    GracefulRestartFamily, LlgrFamily, Safi,
72};
73pub use constants::{EXTENDED_MAX_MESSAGE_LEN, MAX_MESSAGE_LEN};
74pub use error::{DecodeError, EncodeError};
75pub use header::{BgpHeader, MessageType, peek_message_length};
76pub use message::{Message, decode_message, encode_message, encode_message_with_limit};
77pub use notification::NotificationCode;
78pub use notification_msg::NotificationMessage;
79pub use open::OpenMessage;
80pub use route_refresh::{RouteRefreshMessage, RouteRefreshSubtype};
81pub use update::{Ipv4UnicastMode, UpdateMessage};
82
83// ── Routing-domain result enums ──────────────────────────────────────
84//
85// `RpkiValidation` and `AspaValidation` are routing-domain concepts, not
86// wire-format types. They live here because the wire crate is the current
87// lowest common dependency shared by rib, policy, and transport — avoiding
88// a rib → rpki dependency edge. If more shared non-wire types accumulate,
89// extract them into a dedicated domain-types crate.
90
91/// RPKI origin validation state per RFC 6811.
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
93pub enum RpkiValidation {
94    /// A VRP covers the prefix and the origin AS matches.
95    Valid,
96    /// A VRP covers the prefix but the origin AS does not match.
97    Invalid,
98    /// No VRP covers the prefix.
99    #[default]
100    NotFound,
101}
102
103impl std::fmt::Display for RpkiValidation {
104    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105        match self {
106            Self::Valid => write!(f, "valid"),
107            Self::Invalid => write!(f, "invalid"),
108            Self::NotFound => write!(f, "not_found"),
109        }
110    }
111}
112
113impl std::str::FromStr for RpkiValidation {
114    type Err = String;
115
116    fn from_str(s: &str) -> Result<Self, Self::Err> {
117        match s {
118            "valid" => Ok(Self::Valid),
119            "invalid" => Ok(Self::Invalid),
120            "not_found" => Ok(Self::NotFound),
121            other => Err(format!(
122                "unknown RPKI validation state {other:?}, expected \"valid\", \"invalid\", or \"not_found\""
123            )),
124        }
125    }
126}
127
128/// ASPA path verification state per draft-ietf-sidrops-aspa-verification.
129#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
130pub enum AspaValidation {
131    /// All hops in the `AS_PATH` have authorized provider relationships.
132    Valid,
133    /// At least one hop has a proven unauthorized provider relationship.
134    Invalid,
135    /// Verification could not complete due to missing ASPA records.
136    #[default]
137    Unknown,
138}
139
140/// Session context needed to choose and replay ASPA path verification.
141///
142/// ASPA verification is role-aware in
143/// `draft-ietf-sidrops-aspa-verification-25`: routes received from a
144/// provider use downstream verification, while customer/peer/route-server
145/// shapes use upstream verification. Stored routes keep this compact context
146/// so RTR cache updates can revalidate them with the same relationship
147/// semantics used at import time.
148#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
149pub struct AspaValidationContext {
150    /// Neighbor ASN used for the draft v25 leftmost-AS precondition.
151    pub neighbor_asn: Option<u32>,
152    /// Locally configured BGP Role for this eBGP session.
153    pub local_role: Option<BgpRole>,
154    /// True when the local speaker is an RS-client receiving through a
155    /// transparent route server / IX, where the draft exempts the leftmost-AS
156    /// precondition.
157    pub first_as_check_exempt: bool,
158}
159
160impl std::fmt::Display for AspaValidation {
161    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162        match self {
163            Self::Valid => write!(f, "valid"),
164            Self::Invalid => write!(f, "invalid"),
165            Self::Unknown => write!(f, "unknown"),
166        }
167    }
168}
169
170impl std::str::FromStr for AspaValidation {
171    type Err = String;
172
173    fn from_str(s: &str) -> Result<Self, Self::Err> {
174        match s {
175            "valid" => Ok(Self::Valid),
176            "invalid" => Ok(Self::Invalid),
177            "unknown" => Ok(Self::Unknown),
178            other => Err(format!(
179                "unknown ASPA validation state {other:?}, expected \"valid\", \"invalid\", or \"unknown\""
180            )),
181        }
182    }
183}
184
185// Re-export attribute types
186pub use attribute::{
187    AsPath, AsPathSegment, ExtendedCommunity, LargeCommunity, MpReachNlri, MpUnreachNlri, Origin,
188    PathAttribute, RawAttribute, is_private_asn,
189};
190pub use nlri::{Ipv4NlriEntry, Ipv4Prefix, Ipv6Prefix, NlriEntry, Prefix};
191pub use orf::{
192    AddressPrefixOrf, OrfAction, OrfCapEntry, OrfCapType, OrfEntries, OrfEntryGroup, OrfMatch,
193    OrfPayload, OrfSendReceive, OrfType, WhenToRefresh,
194};
195pub use pmsi::{PmsiTunnel, PmsiTunnelIdentifier, PmsiTunnelType};
196pub use update::ParsedUpdate;
197pub use validate::{UpdateError, UpdateValidationOptions, is_valid_ipv6_nexthop};
198
199// Re-export FlowSpec types
200pub use flowspec::{
201    BitmaskMatch, FlowSpecAction, FlowSpecComponent, FlowSpecPrefix, FlowSpecRule,
202    Ipv6PrefixOffset, NumericMatch,
203};
204
205// Re-export EVPN types
206pub use evpn::{
207    EthernetSegmentIdentifier, EthernetTagId, EvpnEadPerEs, EvpnEadPerEvi, EvpnEs, EvpnImet,
208    EvpnIpPrefixRoute, EvpnIpPrefixValue, EvpnMacIp, EvpnRoute, EvpnRouteKey, MacAddress,
209    MplsLabel, RouteDistinguisher, RouteDistinguisherParseError, decode_evpn_nlri,
210    encode_evpn_nlri,
211};
212
213// Well-known communities (RFC 1997 + RFC 7999 + RFC 8326 + RFC 9494)
214/// `NO_EXPORT` community (RFC 1997): routes carrying this community must not
215/// be advertised outside a BGP confederation boundary.
216pub const COMMUNITY_NO_EXPORT: u32 = 0xFFFF_FF01;
217/// `NO_ADVERTISE` community (RFC 1997): routes carrying this community must
218/// not be advertised to any other BGP peer.
219pub const COMMUNITY_NO_ADVERTISE: u32 = 0xFFFF_FF02;
220/// `NO_EXPORT_SUBCONFED` community (RFC 1997): routes carrying this community
221/// must not be advertised to external BGP peers, including confederation
222/// external peers.
223pub const COMMUNITY_NO_EXPORT_SUBCONFED: u32 = 0xFFFF_FF03;
224/// `BLACKHOLE` community (RFC 7999 §5): advisory signal that traffic destined
225/// toward the tagged prefix should be discarded by receivers that explicitly
226/// opted in to honoring the request.
227pub const COMMUNITY_BLACKHOLE: u32 = 0xFFFF_029A;
228/// `GRACEFUL_SHUTDOWN` community (RFC 8326 §3): tags routes on a session
229/// being brought down for maintenance so receivers de-prefer them by
230/// setting `LOCAL_PREF` to a low value (canonical: 0).
231pub const COMMUNITY_GRACEFUL_SHUTDOWN: u32 = 0xFFFF_0000;
232/// `LLGR_STALE` community (RFC 9494 §4.6): marks a route as long-lived stale.
233pub const COMMUNITY_LLGR_STALE: u32 = 0xFFFF_0006;
234/// `NO_LLGR` community (RFC 9494 §4.7): this route must not enter LLGR stale phase.
235pub const COMMUNITY_NO_LLGR: u32 = 0xFFFF_0007;
236
237// Re-export RPKI types
238// (RpkiValidation is defined above in this file)
239
240#[cfg(test)]
241mod well_known_community_tests {
242    use super::*;
243
244    /// Pins the spec-mandated well-known community values. A refactor that
245    /// accidentally renames or repurposes these constants is silently
246    /// behavior-preserving at the type level — this test makes the value
247    /// drift loud.
248    #[test]
249    fn well_known_community_values_match_specs() {
250        assert_eq!(COMMUNITY_NO_EXPORT, 0xFFFF_FF01, "RFC 1997");
251        assert_eq!(COMMUNITY_NO_ADVERTISE, 0xFFFF_FF02, "RFC 1997");
252        assert_eq!(COMMUNITY_NO_EXPORT_SUBCONFED, 0xFFFF_FF03, "RFC 1997");
253        assert_eq!(COMMUNITY_BLACKHOLE, 0xFFFF_029A, "RFC 7999 §5");
254        assert_eq!(COMMUNITY_GRACEFUL_SHUTDOWN, 0xFFFF_0000, "RFC 8326 §3");
255        assert_eq!(COMMUNITY_LLGR_STALE, 0xFFFF_0006, "RFC 9494 §4.6");
256        assert_eq!(COMMUNITY_NO_LLGR, 0xFFFF_0007, "RFC 9494 §4.7");
257    }
258}