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