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/// ROUTE-REFRESH message struct and codec (RFC 2918 / RFC 7313).
57pub mod route_refresh;
58/// UPDATE message struct, codec, and builder.
59pub mod update;
60/// UPDATE attribute semantic validation (RFC 4271 §6.3).
61pub mod validate;
62
63// Re-export primary public API
64pub use capability::{
65 AddPathFamily, AddPathMode, Afi, Capability, ExtendedNextHopFamily, GracefulRestartFamily,
66 LlgrFamily, Safi,
67};
68pub use constants::{EXTENDED_MAX_MESSAGE_LEN, MAX_MESSAGE_LEN};
69pub use error::{DecodeError, EncodeError};
70pub use header::{BgpHeader, MessageType, peek_message_length};
71pub use message::{Message, decode_message, encode_message, encode_message_with_limit};
72pub use notification::NotificationCode;
73pub use notification_msg::NotificationMessage;
74pub use open::OpenMessage;
75pub use route_refresh::{RouteRefreshMessage, RouteRefreshSubtype};
76pub use update::{Ipv4UnicastMode, UpdateMessage};
77
78// ── Routing-domain result enums ──────────────────────────────────────
79//
80// `RpkiValidation` and `AspaValidation` are routing-domain concepts, not
81// wire-format types. They live here because the wire crate is the current
82// lowest common dependency shared by rib, policy, and transport — avoiding
83// a rib → rpki dependency edge. If more shared non-wire types accumulate,
84// extract them into a dedicated domain-types crate.
85
86/// RPKI origin validation state per RFC 6811.
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
88pub enum RpkiValidation {
89 /// A VRP covers the prefix and the origin AS matches.
90 Valid,
91 /// A VRP covers the prefix but the origin AS does not match.
92 Invalid,
93 /// No VRP covers the prefix.
94 #[default]
95 NotFound,
96}
97
98impl std::fmt::Display for RpkiValidation {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 match self {
101 Self::Valid => write!(f, "valid"),
102 Self::Invalid => write!(f, "invalid"),
103 Self::NotFound => write!(f, "not_found"),
104 }
105 }
106}
107
108impl std::str::FromStr for RpkiValidation {
109 type Err = String;
110
111 fn from_str(s: &str) -> Result<Self, Self::Err> {
112 match s {
113 "valid" => Ok(Self::Valid),
114 "invalid" => Ok(Self::Invalid),
115 "not_found" => Ok(Self::NotFound),
116 other => Err(format!(
117 "unknown RPKI validation state {other:?}, expected \"valid\", \"invalid\", or \"not_found\""
118 )),
119 }
120 }
121}
122
123/// ASPA path verification state per draft-ietf-sidrops-aspa-verification.
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
125pub enum AspaValidation {
126 /// All hops in the `AS_PATH` have authorized provider relationships.
127 Valid,
128 /// At least one hop has a proven unauthorized provider relationship.
129 Invalid,
130 /// Verification could not complete due to missing ASPA records.
131 #[default]
132 Unknown,
133}
134
135impl std::fmt::Display for AspaValidation {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 match self {
138 Self::Valid => write!(f, "valid"),
139 Self::Invalid => write!(f, "invalid"),
140 Self::Unknown => write!(f, "unknown"),
141 }
142 }
143}
144
145impl std::str::FromStr for AspaValidation {
146 type Err = String;
147
148 fn from_str(s: &str) -> Result<Self, Self::Err> {
149 match s {
150 "valid" => Ok(Self::Valid),
151 "invalid" => Ok(Self::Invalid),
152 "unknown" => Ok(Self::Unknown),
153 other => Err(format!(
154 "unknown ASPA validation state {other:?}, expected \"valid\", \"invalid\", or \"unknown\""
155 )),
156 }
157 }
158}
159
160// Re-export attribute types
161pub use attribute::{
162 AsPath, AsPathSegment, ExtendedCommunity, LargeCommunity, MpReachNlri, MpUnreachNlri, Origin,
163 PathAttribute, RawAttribute, is_private_asn,
164};
165pub use nlri::{Ipv4NlriEntry, Ipv4Prefix, Ipv6Prefix, NlriEntry, Prefix};
166pub use update::ParsedUpdate;
167pub use validate::{UpdateError, is_valid_ipv6_nexthop};
168
169// Re-export FlowSpec types
170pub use flowspec::{
171 BitmaskMatch, FlowSpecAction, FlowSpecComponent, FlowSpecPrefix, FlowSpecRule,
172 Ipv6PrefixOffset, NumericMatch,
173};
174
175// Re-export EVPN types
176pub use evpn::{
177 EthernetSegmentIdentifier, EthernetTagId, EvpnEadPerEs, EvpnEadPerEvi, EvpnEs, EvpnImet,
178 EvpnIpPrefixRoute, EvpnIpPrefixValue, EvpnMacIp, EvpnRoute, EvpnRouteKey, MacAddress,
179 MplsLabel, RouteDistinguisher, RouteDistinguisherParseError, decode_evpn_nlri,
180 encode_evpn_nlri,
181};
182
183// Well-known communities (RFC 1997 + RFC 8326 + RFC 9494)
184/// `GRACEFUL_SHUTDOWN` community (RFC 8326 §3): tags routes on a session
185/// being brought down for maintenance so receivers de-prefer them by
186/// setting `LOCAL_PREF` to a low value (canonical: 0).
187pub const COMMUNITY_GRACEFUL_SHUTDOWN: u32 = 0xFFFF_0000;
188/// `LLGR_STALE` community (RFC 9494 §4.6): marks a route as long-lived stale.
189pub const COMMUNITY_LLGR_STALE: u32 = 0xFFFF_0006;
190/// `NO_LLGR` community (RFC 9494 §4.7): this route must not enter LLGR stale phase.
191pub const COMMUNITY_NO_LLGR: u32 = 0xFFFF_0007;
192
193// Re-export RPKI types
194// (RpkiValidation is defined above in this file)
195
196#[cfg(test)]
197mod well_known_community_tests {
198 use super::*;
199
200 /// Pins the spec-mandated well-known community values. A refactor that
201 /// accidentally renames or repurposes these constants is silently
202 /// behavior-preserving at the type level — this test makes the value
203 /// drift loud.
204 #[test]
205 fn well_known_community_values_match_specs() {
206 assert_eq!(COMMUNITY_GRACEFUL_SHUTDOWN, 0xFFFF_0000, "RFC 8326 §3");
207 assert_eq!(COMMUNITY_LLGR_STALE, 0xFFFF_0006, "RFC 9494 §4.6");
208 assert_eq!(COMMUNITY_NO_LLGR, 0xFFFF_0007, "RFC 9494 §4.7");
209 }
210}