Skip to main content

bgpkit_parser/parser/bgp/
messages.rs

1use crate::models::*;
2use bytes::{Buf, BufMut, Bytes, BytesMut};
3use std::convert::TryFrom;
4use std::net::Ipv4Addr;
5
6use crate::encoder::sink::{put_u16_len_slice, put_u8_len_slice, with_u16_len};
7use crate::error::{check_max, BgpValidationWarning, EncodingError, ParserError};
8use crate::models::capabilities::{
9    AddPathCapability, BgpCapabilityType, BgpExtendedMessageCapability, BgpRoleCapability,
10    ExtendedNextHopCapability, FourOctetAsCapability, GracefulRestartCapability,
11    MultiprotocolExtensionsCapability, RouteRefreshCapability,
12};
13use crate::models::error::BgpError;
14use crate::parser::bgp::attributes::parse_attributes;
15use crate::parser::{encode_nlri_prefixes, parse_nlri_list, ReadUtils};
16use log::warn;
17use zerocopy::big_endian::{U16, U32};
18use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
19
20/// On-wire BGP OPEN fixed header layout (10 bytes, network byte order).
21#[derive(IntoBytes, FromBytes, KnownLayout, Immutable)]
22#[repr(C)]
23struct RawBgpOpenHeader {
24    version: u8,
25    asn: U16,
26    hold_time: U16,
27    bgp_identifier: U32,
28    opt_params_len: u8,
29}
30
31const _: () = assert!(size_of::<RawBgpOpenHeader>() == 10);
32
33/// On-wire BGP ROUTE-REFRESH fixed body layout (4 bytes, network byte order).
34/// RFC 2918; the reserved byte carries the message subtype per RFC 7313.
35#[derive(IntoBytes, FromBytes, KnownLayout, Immutable)]
36#[repr(C)]
37struct RawRouteRefreshHeader {
38    afi: U16,
39    subtype: u8,
40    safi: u8,
41}
42
43const _: () = assert!(size_of::<RawRouteRefreshHeader>() == 4);
44
45pub(crate) fn read_and_validate_bgp_marker(data: &mut Bytes) -> Result<(), ParserError> {
46    data.has_n_remaining(16)?;
47
48    let mut marker = [0u8; 16];
49    data.copy_to_slice(&mut marker);
50    if marker != [0xFF; 16] {
51        warn!("BGP message marker is not all 0xFF bytes (invalid per RFC 4271)");
52    }
53
54    Ok(())
55}
56
57/// BGP message
58///
59/// Format:
60/// ```text
61/// 0                   1                   2                   3
62/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
63/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
64/// |                                                               |
65/// +                                                               +
66/// |                                                               |
67/// +                                                               +
68/// |                           Marker                              |
69/// +                                                               +
70/// |                                                               |
71/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
72/// |          Length               |      Type     |
73/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
74/// ```
75pub fn parse_bgp_message(
76    data: &mut Bytes,
77    add_path: bool,
78    asn_len: &AsnLength,
79) -> Result<BgpMessage, ParserError> {
80    let total_size = data.len();
81    data.has_n_remaining(19)?;
82    read_and_validate_bgp_marker(data)?;
83
84    /*
85    This 2-octet unsigned integer indicates the total length of the
86    message, including the header in octets.  Thus, it allows one
87    to locate the (Marker field of the) next message in the TCP
88    stream.  The value of the Length field MUST always be at least
89    19 and no greater than 4096, and MAY be further constrained,
90    depending on the message type.  "padding" of extra data after
91    the message is not allowed.  Therefore, the Length field MUST
92    have the smallest value required, given the rest of the
93    message.
94    */
95    let length = data.read_u16()?;
96
97    // Validate message length according to RFC 8654
98    // For now, we allow extended messages for all message types except when we know
99    // for certain that extended messages are not supported.
100    // RFC 8654: Extended messages up to 65535 bytes are allowed for all message types
101    // except OPEN and KEEPALIVE (which remain limited to 4096 bytes).
102    // However, since we're parsing MRT data without session context, we'll be permissive.
103    let max_length = 65535; // RFC 8654 maximum
104    if !(19..=max_length).contains(&length) {
105        return Err(ParserError::ParseError(format!(
106            "invalid BGP message length {length}"
107        )));
108    }
109
110    // Validate length >= 19 before any arithmetic to prevent underflow
111    let length_usize = length as usize;
112    let bgp_msg_length = if length_usize > total_size {
113        total_size.saturating_sub(19)
114    } else {
115        length_usize.saturating_sub(19)
116    };
117
118    let msg_type: BgpMessageType = match BgpMessageType::try_from(data.read_u8()?) {
119        Ok(t) => t,
120        Err(_) => {
121            return Err(ParserError::ParseError(
122                "Unknown BGP Message Type".to_string(),
123            ))
124        }
125    };
126
127    // Additional validation for OPEN and KEEPALIVE messages per RFC 8654
128    // These message types cannot exceed 4096 bytes even with extended message capability
129    match msg_type {
130        BgpMessageType::OPEN | BgpMessageType::KEEPALIVE => {
131            if length > 4096 {
132                return Err(ParserError::ParseError(format!(
133                    "BGP {} message length {} exceeds maximum allowed 4096 bytes (RFC 8654)",
134                    match msg_type {
135                        BgpMessageType::OPEN => "OPEN",
136                        BgpMessageType::KEEPALIVE => "KEEPALIVE",
137                        _ => unreachable!(),
138                    },
139                    length
140                )));
141            }
142        }
143        BgpMessageType::UPDATE | BgpMessageType::NOTIFICATION | BgpMessageType::ROUTE_REFRESH => {
144            // These can be extended messages up to 65535 bytes when capability is negotiated
145            // Since we're parsing MRT data, we allow extended lengths
146        }
147    }
148
149    if data.remaining() != bgp_msg_length {
150        warn!(
151            "BGP message length {} does not match the actual length {} (parsing BGP message)",
152            bgp_msg_length,
153            data.remaining()
154        );
155    }
156    data.has_n_remaining(bgp_msg_length)?;
157    let mut msg_data = data.split_to(bgp_msg_length);
158
159    Ok(match msg_type {
160        BgpMessageType::OPEN => BgpMessage::Open(parse_bgp_open_message(&mut msg_data)?),
161        BgpMessageType::UPDATE => {
162            BgpMessage::Update(parse_bgp_update_message(msg_data, add_path, asn_len)?)
163        }
164        BgpMessageType::NOTIFICATION => {
165            BgpMessage::Notification(parse_bgp_notification_message(msg_data)?)
166        }
167        BgpMessageType::KEEPALIVE => BgpMessage::KeepAlive,
168        BgpMessageType::ROUTE_REFRESH => {
169            BgpMessage::RouteRefresh(parse_bgp_route_refresh_message(&mut msg_data)?)
170        }
171    })
172}
173
174/// Parse BGP ROUTE-REFRESH message.
175///
176/// RFC 2918 body: AFI (2 bytes), Reserved (1 byte), SAFI (1 byte). RFC 7313
177/// redefines the reserved byte as a message subtype. Any trailing bytes (such
178/// as ORF entries per RFC 5291) are retained raw.
179pub fn parse_bgp_route_refresh_message(
180    input: &mut Bytes,
181) -> Result<BgpRouteRefreshMessage, ParserError> {
182    input.has_n_remaining(4)?;
183    let mut header_bytes = [0u8; 4];
184    input.copy_to_slice(&mut header_bytes);
185    // Single bounds check via zerocopy instead of three sequential cursor reads.
186    let raw = RawRouteRefreshHeader::ref_from_bytes(&header_bytes)
187        .expect("header_bytes is exactly 4 bytes with no alignment requirement");
188
189    Ok(BgpRouteRefreshMessage {
190        afi: raw.afi.get(),
191        subtype: raw.subtype,
192        safi: raw.safi,
193        data: input.split_to(input.remaining()).to_vec(),
194    })
195}
196
197impl BgpRouteRefreshMessage {
198    pub fn encode(&self) -> Bytes {
199        let raw = RawRouteRefreshHeader {
200            afi: U16::new(self.afi),
201            subtype: self.subtype,
202            safi: self.safi,
203        };
204        let mut bytes = BytesMut::with_capacity(4 + self.data.len());
205        bytes.put_slice(raw.as_bytes());
206        bytes.put_slice(&self.data);
207        bytes.freeze()
208    }
209}
210
211/// Parse BGP NOTIFICATION message.
212///
213/// The BGP NOTIFICATION messages contains BGP error codes received from a connected BGP router. The
214/// error code is parsed into [BgpError] data structure and any unknown codes will produce warning
215/// messages, but not critical errors.
216///
217pub fn parse_bgp_notification_message(
218    mut input: Bytes,
219) -> Result<BgpNotificationMessage, ParserError> {
220    let error_code = input.read_u8()?;
221    let error_subcode = input.read_u8()?;
222
223    Ok(BgpNotificationMessage {
224        error: BgpError::new(error_code, error_subcode),
225        data: input.read_n_bytes(input.len())?,
226    })
227}
228
229impl BgpNotificationMessage {
230    pub fn encode(&self) -> Bytes {
231        let mut buf = BytesMut::new();
232        let (code, subcode) = self.error.get_codes();
233        buf.put_u8(code);
234        buf.put_u8(subcode);
235        buf.put_slice(&self.data);
236        buf.freeze()
237    }
238}
239
240/// Parse BGP OPEN message.
241///
242/// The parsing of BGP OPEN message also includes decoding the BGP capabilities.
243///
244/// RFC 4271: <https://datatracker.ietf.org/doc/html/rfc4271>
245/// ```text
246///       0                   1                   2                   3
247///       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
248///       +-+-+-+-+-+-+-+-+
249///       |    Version    |
250///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
251///       |     My Autonomous System      |
252///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
253///       |           Hold Time           |
254///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
255///       |                         BGP Identifier                        |
256///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
257///       | Opt Parm Len  |
258///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
259///       |                                                               |
260///       |             Optional Parameters (variable)                    |
261///       |                                                               |
262///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
263///
264///       0                   1
265///       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
266///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...
267///       |  Parm. Type   | Parm. Length  |  Parameter Value (variable)
268///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...
269/// ```
270pub fn parse_bgp_open_message(input: &mut Bytes) -> Result<BgpOpenMessage, ParserError> {
271    input.has_n_remaining(10)?;
272    let mut header_bytes = [0u8; 10];
273    input.copy_to_slice(&mut header_bytes);
274    // Single bounds check via zerocopy instead of five sequential cursor reads.
275    let raw = RawBgpOpenHeader::ref_from_bytes(&header_bytes)
276        .expect("header_bytes is exactly 10 bytes with no alignment requirement");
277
278    let version = raw.version;
279    let asn = Asn::new_16bit(raw.asn.get());
280    let hold_time = raw.hold_time.get();
281    let bgp_identifier = Ipv4Addr::from(raw.bgp_identifier.get());
282    let mut opt_params_len: u16 = raw.opt_params_len as u16;
283
284    let mut extended_length = false;
285    let mut first = true;
286
287    let mut params: Vec<OptParam> = vec![];
288    while input.remaining() >= 2 {
289        let mut param_type = input.read_u8()?;
290        if first {
291            if opt_params_len == 0 && param_type == 255 {
292                return Err(ParserError::ParseError(
293                    "RFC 9072 violation: Non-Extended Optional Parameters Length must not be 0 when using extended format".to_string()
294                ));
295            }
296            // first parameter, check if it is extended length message
297            if opt_params_len != 0 && param_type == 255 {
298                // RFC 9072: https://datatracker.ietf.org/doc/rfc9072/
299                //
300                // 0                   1                   2                   3
301                // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
302                //     +-+-+-+-+-+-+-+-+
303                //     |    Version    |
304                //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
305                //     |     My Autonomous System      |
306                //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
307                //     |           Hold Time           |
308                //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
309                //     |                         BGP Identifier                        |
310                //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
311                //     |Non-Ext OP Len.|Non-Ext OP Type|  Extended Opt. Parm. Length   |
312                //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
313                //     |                                                               |
314                //     |             Optional Parameters (variable)                    |
315                //     |                                                               |
316                //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
317                //
318                //         Figure 1: Extended Encoding OPEN Format
319                extended_length = true;
320                opt_params_len = input.read_u16()?;
321                if opt_params_len == 0 {
322                    break;
323                }
324                // let pos_end = input.position() + opt_params_len as u64;
325                if input.remaining() != opt_params_len as usize {
326                    warn!(
327                    "BGP open message length {} does not match the actual length {} (parsing BGP OPEN message)",
328                    opt_params_len,
329                    input.remaining()
330                );
331                }
332
333                param_type = input.read_u8()?;
334            }
335            first = false;
336        }
337        // reaching here means all the remain params are regular non-extended-length parameters
338
339        let param_len = match extended_length {
340            true => input.read_u16()?,
341            false => input.read_u8()? as u16,
342        };
343
344        // https://tools.ietf.org/html/rfc3392
345        // https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-11
346
347        let param_value = match param_type {
348            2 => {
349                let mut capacities = vec![];
350
351                // Split off only the bytes for this parameter to avoid consuming other parameters
352                input.has_n_remaining(param_len as usize)?;
353                let mut param_data = input.split_to(param_len as usize);
354
355                while param_data.remaining() >= 2 {
356                    // capability codes:
357                    // https://www.iana.org/assignments/capability-codes/capability-codes.xhtml#capability-codes-2
358                    let code = param_data.read_u8()?;
359                    let len = param_data.read_u8()? as u16; // Capability length is ALWAYS 1 byte per RFC 5492
360
361                    let capability_data = param_data.read_n_bytes(len as usize)?;
362                    let capability_type = BgpCapabilityType::from(code);
363
364                    // Parse specific capability types with fallback to raw bytes
365                    macro_rules! parse_capability {
366                        ($parser:path, $variant:ident) => {
367                            match $parser(Bytes::from(capability_data.clone())) {
368                                Ok(parsed) => CapabilityValue::$variant(parsed),
369                                Err(_) => CapabilityValue::Raw(capability_data),
370                            }
371                        };
372                    }
373
374                    let capability_value = match capability_type {
375                        BgpCapabilityType::MULTIPROTOCOL_EXTENSIONS_FOR_BGP_4 => {
376                            parse_capability!(
377                                MultiprotocolExtensionsCapability::parse,
378                                MultiprotocolExtensions
379                            )
380                        }
381                        BgpCapabilityType::ROUTE_REFRESH_CAPABILITY_FOR_BGP_4 => {
382                            parse_capability!(RouteRefreshCapability::parse, RouteRefresh)
383                        }
384                        BgpCapabilityType::EXTENDED_NEXT_HOP_ENCODING => {
385                            parse_capability!(ExtendedNextHopCapability::parse, ExtendedNextHop)
386                        }
387                        BgpCapabilityType::GRACEFUL_RESTART_CAPABILITY => {
388                            parse_capability!(GracefulRestartCapability::parse, GracefulRestart)
389                        }
390                        BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY => {
391                            parse_capability!(FourOctetAsCapability::parse, FourOctetAs)
392                        }
393                        BgpCapabilityType::ADD_PATH_CAPABILITY => {
394                            parse_capability!(AddPathCapability::parse, AddPath)
395                        }
396                        BgpCapabilityType::BGP_ROLE => {
397                            parse_capability!(BgpRoleCapability::parse, BgpRole)
398                        }
399                        BgpCapabilityType::BGP_EXTENDED_MESSAGE => {
400                            parse_capability!(
401                                BgpExtendedMessageCapability::parse,
402                                BgpExtendedMessage
403                            )
404                        }
405                        _ => CapabilityValue::Raw(capability_data),
406                    };
407
408                    capacities.push(Capability {
409                        ty: capability_type,
410                        value: capability_value,
411                    });
412                }
413
414                ParamValue::Capacities(capacities)
415            }
416            _ => {
417                // unsupported param, read as raw bytes
418                let bytes = input.read_n_bytes(param_len as usize)?;
419                ParamValue::Raw(bytes)
420            }
421        };
422        params.push(OptParam {
423            param_type,
424            param_value,
425        });
426    }
427
428    Ok(BgpOpenMessage {
429        version,
430        asn,
431        hold_time,
432        bgp_identifier,
433        extended_length,
434        opt_params: params,
435    })
436}
437
438fn encode_bgp_open_param_value(param: &OptParam) -> Result<Bytes, EncodingError> {
439    let mut buf = BytesMut::new();
440    match &param.param_value {
441        ParamValue::Capacities(capacities) => {
442            for cap in capacities {
443                buf.put_u8(cap.ty.into());
444                let encoded_value = match &cap.value {
445                    CapabilityValue::MultiprotocolExtensions(mp) => mp.encode(),
446                    CapabilityValue::RouteRefresh(rr) => rr.encode(),
447                    CapabilityValue::ExtendedNextHop(enh) => enh.encode(),
448                    CapabilityValue::GracefulRestart(gr) => gr.encode(),
449                    CapabilityValue::FourOctetAs(foa) => foa.encode(),
450                    CapabilityValue::AddPath(ap) => ap.encode(),
451                    CapabilityValue::BgpRole(br) => br.encode(),
452                    CapabilityValue::BgpExtendedMessage(bem) => bem.encode(),
453                    CapabilityValue::Raw(raw) => Bytes::from(raw.clone()),
454                };
455                put_u8_len_slice(&mut buf, "BGP capability value length", &encoded_value)?;
456            }
457        }
458        ParamValue::Raw(bytes) => buf.put_slice(bytes),
459    }
460    Ok(buf.freeze())
461}
462
463impl BgpOpenMessage {
464    pub fn encode(&self) -> Result<Bytes, EncodingError> {
465        let encoded_params: Vec<(u8, Bytes)> = self
466            .opt_params
467            .iter()
468            .map(|param| {
469                // RFC 9072 reserves optional parameter type 255 as the
470                // extended-length marker; emitting it as a real parameter
471                // would be misparsed on the receiving side.
472                if param.param_type == u8::MAX {
473                    return Err(EncodingError::unencodable(
474                        "BGP OPEN optional parameter type",
475                        "type 255 is reserved by RFC 9072 as the extended-length marker",
476                    ));
477                }
478                Ok((param.param_type, encode_bgp_open_param_value(param)?))
479            })
480            .collect::<Result<_, _>>()?;
481
482        let values_len: usize = encoded_params.iter().map(|(_, value)| value.len()).sum();
483        // Non-extended framing spends 2 header octets (type + 1-octet length) per
484        // parameter; if that would overflow the single-octet aggregate length field
485        // we must switch to RFC 9072 extended framing (3 header octets each).
486        let non_extended_params_len = 2 * encoded_params.len() + values_len;
487        let use_extended_length =
488            self.extended_length || non_extended_params_len > u8::MAX as usize;
489        let per_param_header = if use_extended_length { 3 } else { 2 };
490        let encoded_params_len = per_param_header * encoded_params.len() + values_len;
491
492        let mut buf = BytesMut::with_capacity(
493            size_of::<RawBgpOpenHeader>()
494                + encoded_params_len
495                + if use_extended_length { 3 } else { 0 },
496        );
497        let raw_header = RawBgpOpenHeader {
498            version: self.version,
499            asn: U16::new(self.asn.into()),
500            hold_time: U16::new(self.hold_time),
501            bgp_identifier: U32::new(u32::from(self.bgp_identifier)),
502            opt_params_len: if use_extended_length {
503                u8::MAX
504            } else {
505                encoded_params_len as u8
506            },
507        };
508        buf.extend_from_slice(raw_header.as_bytes());
509
510        if use_extended_length {
511            // RFC 9072: type 255 signals a two-octet aggregate length and
512            // two-octet lengths for each optional parameter.
513            check_max(
514                "BGP OPEN extended optional parameters total length",
515                encoded_params_len,
516                u16::MAX as usize,
517            )?;
518            buf.put_u8(u8::MAX);
519            buf.put_u16(encoded_params_len as u16);
520        }
521
522        for (param_type, value) in encoded_params {
523            buf.put_u8(param_type);
524            if use_extended_length {
525                // Cannot overflow: the aggregate check above bounds
526                // encoded_params_len, and each value.len() <= encoded_params_len - 3.
527                debug_assert!(value.len() <= u16::MAX as usize);
528                buf.put_u16(value.len() as u16);
529            } else {
530                // Fits in a u8: use_extended_length is set above whenever the
531                // non-extended framing (2 + value.len() per param) would exceed u8::MAX.
532                buf.put_u8(value.len() as u8);
533            }
534            buf.put_slice(&value);
535        }
536        Ok(buf.freeze())
537    }
538}
539
540/// read nlri portion of a bgp update message.
541///
542/// Returns `Ok(vec![])` for empty NLRI. Returns `Err` for malformed NLRI
543/// (invalid prefix lengths, truncated data), so that the caller
544/// in `parse_bgp_update_message` can convert it to a `MalformedNlri` warning.
545fn read_nlri(input: Bytes, afi: &Afi, add_path: bool) -> Result<Vec<NetworkPrefix>, ParserError> {
546    let length = input.len();
547    if length == 0 {
548        return Ok(vec![]);
549    }
550    if length == 1 && input[0] != 0 {
551        // A single non-zero byte cannot be a valid NLRI: a valid 1-byte NLRI
552        // encodes only the default route (prefix length 0, no prefix octets).
553        warn!("seeing strange one-byte NLRI field (parsing NLRI in BGP UPDATE message)");
554        return Err(ParserError::ParseError(
555            "one-byte NLRI field with non-zero value is not a valid encoding".to_string(),
556        ));
557    }
558
559    parse_nlri_list(input, add_path, afi)
560}
561
562/// read bgp update message.
563///
564/// RFC: <https://tools.ietf.org/html/rfc4271#section-4.3>
565///
566/// Per RFC 7606, NLRI parse errors are non-fatal: the message is returned with
567/// partial data and a [`BgpValidationWarning::MalformedNlri`] appended to
568/// `attributes.validation_warnings`. Callers that want RFC 7606 treat-as-withdrawal
569/// semantics can inspect the warnings and act accordingly. Attribute-section
570/// framing errors (wrong length fields, truncated data) remain fatal.
571pub fn parse_bgp_update_message(
572    mut input: Bytes,
573    add_path: bool,
574    asn_len: &AsnLength,
575) -> Result<BgpUpdateMessage, ParserError> {
576    // NOTE: AFI for routes outside attributes are IPv4 ONLY.
577    let afi = Afi::Ipv4;
578
579    // parse withdrawn prefixes NLRI
580    let withdrawn_bytes_length_raw = input.read_u16()?;
581    let withdrawn_bytes_length = withdrawn_bytes_length_raw as usize;
582    input.has_n_remaining(withdrawn_bytes_length)?;
583    let withdrawn_bytes = input.split_to(withdrawn_bytes_length);
584    let (withdrawn_prefixes, withdrawn_nlri_error) =
585        match read_nlri(withdrawn_bytes.clone(), &afi, add_path) {
586            Ok(pfxs) => (pfxs, None),
587            Err(e) => (
588                Vec::new(),
589                Some(BgpValidationWarning::MalformedNlri {
590                    nlri_type: "withdrawn",
591                    reason: e.to_string(),
592                    raw_bytes: withdrawn_bytes.to_vec(),
593                }),
594            ),
595        };
596
597    // parse attributes
598    let attribute_length_raw = input.read_u16()?;
599    // Defensive check: ensure attribute_length fits in usize
600    // u16 to usize conversion is always safe on 32/64-bit platforms,
601    // but this check ensures safety on all architectures
602    let attribute_length = attribute_length_raw as usize;
603
604    input.has_n_remaining(attribute_length)?;
605    let attr_data_slice = input.split_to(attribute_length);
606    let mut attributes = parse_attributes(attr_data_slice, asn_len, add_path, None, None, None)?;
607
608    // parse announced prefixes nlri.
609    // the remaining bytes are announced prefixes.
610    let announced_bytes_present = !input.is_empty();
611    let (announced_prefixes, announced_nlri_error) = match read_nlri(input.clone(), &afi, add_path)
612    {
613        Ok(pfxs) => (pfxs, None),
614        Err(e) => (
615            Vec::new(),
616            Some(BgpValidationWarning::MalformedNlri {
617                nlri_type: "announced",
618                reason: e.to_string(),
619                raw_bytes: input.to_vec(),
620            }),
621        ),
622    };
623
624    // validate mandatory attributes.
625    // Use `announced_bytes_present` (wire-level) rather than
626    // `!announced_prefixes.is_empty()` (parse result) so that a malformed
627    // NLRI section still triggers mandatory-attribute validation — the
628    // UPDATE clearly intended to announce routes.
629    let is_announcement =
630        announced_bytes_present || attributes.has_attr(AttrType::MP_REACHABLE_NLRI);
631    let has_standard_nlri = announced_bytes_present;
632    attributes.check_mandatory_attributes(is_announcement, has_standard_nlri);
633
634    // Attach NLRI parse warnings (RFC 7606 §5.3 treat-as-withdrawal evidence)
635    if let Some(w) = withdrawn_nlri_error {
636        attributes.add_validation_warning(w);
637    }
638    if let Some(w) = announced_nlri_error {
639        attributes.add_validation_warning(w);
640    }
641
642    Ok(BgpUpdateMessage {
643        withdrawn_prefixes,
644        attributes,
645        announced_prefixes,
646    })
647}
648
649impl BgpUpdateMessage {
650    pub fn encode(&self, asn_len: AsnLength) -> Result<Bytes, EncodingError> {
651        let mut bytes = BytesMut::new();
652
653        // withdrawn prefixes
654        let withdrawn_bytes = encode_nlri_prefixes(&self.withdrawn_prefixes);
655        put_u16_len_slice(
656            &mut bytes,
657            "BGP UPDATE withdrawn routes length",
658            &withdrawn_bytes,
659        )?;
660
661        // attributes
662        with_u16_len(&mut bytes, "BGP UPDATE total path attribute length", |b| {
663            self.attributes.encode_to(asn_len, b)
664        })?;
665
666        bytes.extend(encode_nlri_prefixes(&self.announced_prefixes));
667        Ok(bytes.freeze())
668    }
669
670    /// Check if this is an end-of-rib message.
671    ///
672    /// <https://datatracker.ietf.org/doc/html/rfc4724#section-2>
673    /// End-of-rib message is a special update message that contains no NLRI or withdrawal NLRI prefixes.
674    pub fn is_end_of_rib(&self) -> bool {
675        // there are two cases for end-of-rib message:
676        // 1. IPv4 unicast address family: no announced, no withdrawn, no attributes
677        // 2. Other cases: no announced, no withdrawal, only MP_UNREACH_NRLI with no prefixes
678
679        if !self.announced_prefixes.is_empty() || !self.withdrawn_prefixes.is_empty() {
680            // has announced or withdrawal IPv4 unicast prefixes:
681            // definitely not end-of-rib
682
683            return false;
684        }
685
686        if self.attributes.inner.is_empty() {
687            // no attributes, no prefixes:
688            // case 1 end-of-rib
689            return true;
690        }
691
692        // has some attributes, it can only be withdrawal with no prefixes
693
694        if self.attributes.inner.len() > 1 {
695            // has more than one attributes, not end-of-rib
696            return false;
697        }
698
699        // has only one attribute, check if it is withdrawal attribute
700        if let AttributeValue::MpUnreachNlri(nlri) = &self.attributes.inner.first().unwrap().value {
701            if nlri.prefixes.is_empty() {
702                // the only attribute is MP_UNREACH_NLRI with no prefixes:
703                // case 2 end-of-rib
704                return true;
705            }
706        }
707
708        // all other cases: not end-of-rib
709        false
710    }
711}
712
713impl BgpMessage {
714    /// BGP marker value: 16 bytes of 0xFF (RFC 4271)
715    const MARKER: [u8; 16] = [0xFF; 16];
716
717    pub fn encode(&self, asn_len: AsnLength) -> Result<Bytes, EncodingError> {
718        let mut bytes = BytesMut::new();
719        // RFC 4271: Marker is 16 bytes of 0xFF
720        bytes.put_slice(&Self::MARKER);
721
722        let (msg_type, msg_bytes) = match self {
723            BgpMessage::Open(msg) => (BgpMessageType::OPEN, msg.encode()?),
724            BgpMessage::Update(msg) => (BgpMessageType::UPDATE, msg.encode(asn_len)?),
725            BgpMessage::Notification(msg) => (BgpMessageType::NOTIFICATION, msg.encode()),
726            BgpMessage::KeepAlive => (BgpMessageType::KEEPALIVE, Bytes::new()),
727            BgpMessage::RouteRefresh(msg) => (BgpMessageType::ROUTE_REFRESH, msg.encode()),
728        };
729
730        // msg total bytes length = msg bytes + 16 bytes marker + 2 bytes length + 1 byte type
731        let total_len = msg_bytes.len() + 16 + 2 + 1;
732        check_max("BGP message total length", total_len, u16::MAX as usize)?;
733        bytes.put_u16(total_len as u16);
734        bytes.put_u8(msg_type as u8);
735        bytes.put_slice(&msg_bytes);
736        Ok(bytes.freeze())
737    }
738}
739
740impl From<&BgpElem> for BgpUpdateMessage {
741    fn from(elem: &BgpElem) -> Self {
742        BgpUpdateMessage {
743            withdrawn_prefixes: vec![],
744            attributes: Attributes::from(elem),
745            announced_prefixes: vec![],
746        }
747    }
748}
749
750impl From<BgpUpdateMessage> for BgpMessage {
751    fn from(value: BgpUpdateMessage) -> Self {
752        BgpMessage::Update(value)
753    }
754}
755
756#[cfg(test)]
757mod tests {
758    use super::*;
759    use std::net::Ipv4Addr;
760    use std::str::FromStr;
761
762    #[test]
763    fn test_end_of_rib() {
764        // No prefixes and empty attributes: end-of-rib
765        let attrs = Attributes::default();
766        let msg = BgpUpdateMessage {
767            withdrawn_prefixes: vec![],
768            attributes: attrs,
769            announced_prefixes: vec![],
770        };
771        assert!(msg.is_end_of_rib());
772
773        // single MP_UNREACH_NLRI attribute with no prefixes: end-of-rib
774        let attrs = Attributes::from_iter(vec![AttributeValue::MpUnreachNlri(Nlri {
775            afi: Afi::Ipv4,
776            safi: Safi::Unicast,
777            next_hop: None,
778            prefixes: vec![],
779            labeled_prefixes: None,
780            link_state_nlris: None,
781            flowspec_nlris: None,
782        })]);
783        let msg = BgpUpdateMessage {
784            withdrawn_prefixes: vec![],
785            attributes: attrs,
786            announced_prefixes: vec![],
787        };
788        assert!(msg.is_end_of_rib());
789
790        // message with announced prefixes
791        let prefix = NetworkPrefix::from_str("192.168.1.0/24").unwrap();
792        let attrs = Attributes::default();
793        let msg = BgpUpdateMessage {
794            withdrawn_prefixes: vec![],
795            attributes: attrs,
796            announced_prefixes: vec![prefix],
797        };
798        assert!(!msg.is_end_of_rib());
799
800        // message with withdrawn prefixes
801        let prefix = NetworkPrefix::from_str("192.168.1.0/24").unwrap();
802        let attrs = Attributes::default();
803        let msg = BgpUpdateMessage {
804            withdrawn_prefixes: vec![prefix],
805            attributes: attrs,
806            announced_prefixes: vec![],
807        };
808        assert!(!msg.is_end_of_rib());
809
810        // NLRI attribute with empty prefixes: NOT end-of-rib
811        let attrs = Attributes::from_iter(vec![AttributeValue::MpReachNlri(Nlri {
812            afi: Afi::Ipv4,
813            safi: Safi::Unicast,
814            next_hop: None,
815            prefixes: vec![],
816            labeled_prefixes: None,
817            link_state_nlris: None,
818            flowspec_nlris: None,
819        })]);
820        let msg = BgpUpdateMessage {
821            withdrawn_prefixes: vec![],
822            attributes: attrs,
823            announced_prefixes: vec![],
824        };
825        assert!(!msg.is_end_of_rib());
826
827        // NLRI attribute with non-empty prefixes
828        let attrs = Attributes::from_iter(vec![AttributeValue::MpReachNlri(Nlri {
829            afi: Afi::Ipv4,
830            safi: Safi::Unicast,
831            next_hop: None,
832            prefixes: vec![prefix],
833            labeled_prefixes: None,
834            link_state_nlris: None,
835            flowspec_nlris: None,
836        })]);
837        let msg = BgpUpdateMessage {
838            withdrawn_prefixes: vec![],
839            attributes: attrs,
840            announced_prefixes: vec![],
841        };
842        assert!(!msg.is_end_of_rib());
843
844        // Unreachable NLRI attribute with non-empty prefixes
845        let attrs = Attributes::from_iter(vec![AttributeValue::MpUnreachNlri(Nlri {
846            afi: Afi::Ipv4,
847            safi: Safi::Unicast,
848            next_hop: None,
849            prefixes: vec![prefix],
850            labeled_prefixes: None,
851            link_state_nlris: None,
852            flowspec_nlris: None,
853        })]);
854        let msg = BgpUpdateMessage {
855            withdrawn_prefixes: vec![],
856            attributes: attrs,
857            announced_prefixes: vec![],
858        };
859        assert!(!msg.is_end_of_rib());
860
861        // message with more than one attributes
862        let attrs = Attributes::from_iter(vec![
863            AttributeValue::MpUnreachNlri(Nlri {
864                afi: Afi::Ipv4,
865                safi: Safi::Unicast,
866                next_hop: None,
867                prefixes: vec![],
868                labeled_prefixes: None,
869                link_state_nlris: None,
870                flowspec_nlris: None,
871            }),
872            AttributeValue::AtomicAggregate,
873        ]);
874        let msg = BgpUpdateMessage {
875            withdrawn_prefixes: vec![],
876            attributes: attrs,
877            announced_prefixes: vec![],
878        };
879        assert!(!msg.is_end_of_rib());
880    }
881
882    #[test]
883    fn test_invalid_length() {
884        let bytes = Bytes::from_static(&[
885            0x00, 0x00, 0x00, 0x00, // marker
886            0x00, 0x00, 0x00, 0x00, // marker
887            0x00, 0x00, 0x00, 0x00, // marker
888            0x00, 0x00, 0x00, 0x00, // marker
889            0x00, 0x00, // length
890            0x05, // type
891        ]);
892        let mut data = bytes.clone();
893        assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits16).is_err());
894
895        let bytes = Bytes::from_static(&[
896            0x00, 0x00, 0x00, 0x00, // marker
897            0x00, 0x00, 0x00, 0x00, // marker
898            0x00, 0x00, 0x00, 0x00, // marker
899            0x00, 0x00, 0x00, 0x00, // marker
900            0x00, 0x28, // length
901            0x05, // type
902        ]);
903        let mut data = bytes.clone();
904        assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits16).is_err());
905    }
906
907    #[test]
908    fn test_invalid_type() {
909        let bytes = Bytes::from_static(&[
910            0xFF, 0xFF, 0xFF, 0xFF, // marker (valid RFC 4271)
911            0xFF, 0xFF, 0xFF, 0xFF, // marker
912            0xFF, 0xFF, 0xFF, 0xFF, // marker
913            0xFF, 0xFF, 0xFF, 0xFF, // marker
914            0x00, 0x28, // length
915            0x06, // type (unassigned)
916        ]);
917        let mut data = bytes.clone();
918        assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits16).is_err());
919    }
920
921    #[test]
922    fn test_parse_bgp_route_refresh_message() {
923        // BGP portion of MRT record 4978 in
924        // https://data.ris.ripe.net/rrc00/2012.07/updates.20120718.2020.gz
925        let bytes = Bytes::from_static(&[
926            0xFF, 0xFF, 0xFF, 0xFF, // marker
927            0xFF, 0xFF, 0xFF, 0xFF, // marker
928            0xFF, 0xFF, 0xFF, 0xFF, // marker
929            0xFF, 0xFF, 0xFF, 0xFF, // marker
930            0x00, 0x17, // length = 23
931            0x05, // type = ROUTE-REFRESH
932            0x00, 0x01, // AFI = 1 (IPv4)
933            0x00, // reserved / subtype
934            0x01, // SAFI = 1 (unicast)
935        ]);
936        let mut data = bytes.clone();
937        let msg = parse_bgp_message(&mut data, false, &AsnLength::Bits32).unwrap();
938        let refresh = match &msg {
939            BgpMessage::RouteRefresh(refresh) => refresh,
940            _ => panic!("expected RouteRefresh, got {msg:?}"),
941        };
942        assert_eq!(msg.msg_type(), BgpMessageType::ROUTE_REFRESH);
943        assert_eq!(refresh.afi, 1);
944        assert_eq!(refresh.subtype, 0);
945        assert_eq!(refresh.safi, 1);
946        assert!(refresh.data.is_empty());
947        assert_eq!(refresh.afi(), Some(Afi::Ipv4));
948        assert_eq!(refresh.safi(), Some(Safi::Unicast));
949
950        // encoding must roundtrip byte-identically
951        let encoded = msg.encode(AsnLength::Bits32).unwrap();
952        assert_eq!(encoded, bytes);
953    }
954
955    #[test]
956    fn test_parse_bgp_route_refresh_message_with_orf_data() {
957        // RFC 7313 BoRR subtype with trailing ORF bytes; unknown AFI/SAFI values
958        // must be preserved rather than rejected.
959        let bytes = Bytes::from_static(&[
960            0xFF, 0xFF, 0xFF, 0xFF, // marker
961            0xFF, 0xFF, 0xFF, 0xFF, // marker
962            0xFF, 0xFF, 0xFF, 0xFF, // marker
963            0xFF, 0xFF, 0xFF, 0xFF, // marker
964            0x00, 0x1A, // length = 26
965            0x05, // type = ROUTE-REFRESH
966            0x00, 0x19, // AFI = 25 (L2VPN, not in the Afi enum)
967            0x01, // subtype = BoRR
968            0x41, // SAFI = 65 (VPLS, not in the Safi enum)
969            0xDE, 0xAD, 0xBE, // trailing ORF bytes, kept raw
970        ]);
971        let mut data = bytes.clone();
972        let msg = parse_bgp_message(&mut data, false, &AsnLength::Bits32).unwrap();
973        let refresh = match &msg {
974            BgpMessage::RouteRefresh(refresh) => refresh,
975            _ => panic!("expected RouteRefresh, got {msg:?}"),
976        };
977        assert_eq!(refresh.afi, 25);
978        assert_eq!(refresh.subtype, 1);
979        assert_eq!(refresh.safi, 65);
980        assert_eq!(refresh.data, vec![0xDE, 0xAD, 0xBE]);
981        assert_eq!(refresh.afi(), None);
982        assert_eq!(refresh.safi(), None);
983
984        let encoded = msg.encode(AsnLength::Bits32).unwrap();
985        assert_eq!(encoded, bytes);
986    }
987
988    #[test]
989    fn test_parse_bgp_route_refresh_message_unknown_subtype() {
990        // RFC 7313 defines subtypes 0 (normal), 1 (BoRR), and 2 (EoRR); other
991        // values are unassigned. The parser retains an unknown subtype raw
992        // instead of rejecting the message, so the record round-trips.
993        let bytes = Bytes::from_static(&[
994            0xFF, 0xFF, 0xFF, 0xFF, // marker
995            0xFF, 0xFF, 0xFF, 0xFF, // marker
996            0xFF, 0xFF, 0xFF, 0xFF, // marker
997            0xFF, 0xFF, 0xFF, 0xFF, // marker
998            0x00, 0x17, // length = 23
999            0x05, // type = ROUTE-REFRESH
1000            0x00, 0x01, // AFI = 1 (IPv4)
1001            0x03, // subtype = 3 (unassigned)
1002            0x01, // SAFI = 1 (unicast)
1003        ]);
1004        let mut data = bytes.clone();
1005        let msg = parse_bgp_message(&mut data, false, &AsnLength::Bits32).unwrap();
1006        let refresh = match &msg {
1007            BgpMessage::RouteRefresh(refresh) => refresh,
1008            _ => panic!("expected RouteRefresh, got {msg:?}"),
1009        };
1010        assert_eq!(refresh.afi, 1);
1011        assert_eq!(refresh.subtype, 3);
1012        assert_eq!(refresh.safi, 1);
1013        assert!(refresh.data.is_empty());
1014
1015        let encoded = msg.encode(AsnLength::Bits32).unwrap();
1016        assert_eq!(encoded, bytes);
1017    }
1018
1019    #[test]
1020    fn test_parse_bgp_route_refresh_message_truncated() {
1021        // body shorter than the 4-byte fixed part must error, not panic
1022        let bytes = Bytes::from_static(&[
1023            0xFF, 0xFF, 0xFF, 0xFF, // marker
1024            0xFF, 0xFF, 0xFF, 0xFF, // marker
1025            0xFF, 0xFF, 0xFF, 0xFF, // marker
1026            0xFF, 0xFF, 0xFF, 0xFF, // marker
1027            0x00, 0x15, // length = 21 (only 2 body bytes)
1028            0x05, // type = ROUTE-REFRESH
1029            0x00, 0x01, // truncated body
1030        ]);
1031        let mut data = bytes.clone();
1032        assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits32).is_err());
1033    }
1034
1035    #[test]
1036    fn test_bgp_message_length_underflow_protection() {
1037        // Test that length values less than 19 are properly rejected
1038        // without causing arithmetic underflow
1039        for len in [0u16, 1, 18] {
1040            let bytes = Bytes::from(vec![
1041                0xFF,
1042                0xFF,
1043                0xFF,
1044                0xFF, // marker
1045                0xFF,
1046                0xFF,
1047                0xFF,
1048                0xFF, // marker
1049                0xFF,
1050                0xFF,
1051                0xFF,
1052                0xFF, // marker
1053                0xFF,
1054                0xFF,
1055                0xFF,
1056                0xFF, // marker
1057                (len >> 8) as u8,
1058                (len & 0xFF) as u8, // length field
1059                0x01,               // type = OPEN
1060            ]);
1061            let mut data = bytes.clone();
1062            let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1063            assert!(
1064                result.is_err(),
1065                "Length {} should be rejected as invalid",
1066                len
1067            );
1068        }
1069    }
1070
1071    #[test]
1072    fn test_bgp_marker_encoding_rfc4271() {
1073        // Test that BgpMessage::encode produces correct RFC 4271 marker (all 0xFF)
1074        let msg = BgpMessage::KeepAlive;
1075        let encoded = msg.encode(AsnLength::Bits16).unwrap();
1076
1077        // First 16 bytes should be all 0xFF
1078        assert_eq!(
1079            &encoded[..16],
1080            &[0xFF; 16],
1081            "BGP marker should be all 0xFF bytes"
1082        );
1083    }
1084
1085    #[test]
1086    fn test_bgp_marker_validation() {
1087        // Test that message with valid marker (all 0xFF) parses correctly
1088        let valid_bytes = Bytes::from(vec![
1089            0xFF, 0xFF, 0xFF, 0xFF, // marker
1090            0xFF, 0xFF, 0xFF, 0xFF, // marker
1091            0xFF, 0xFF, 0xFF, 0xFF, // marker
1092            0xFF, 0xFF, 0xFF, 0xFF, // marker
1093            0x00, 0x13, // length = 19 (minimum)
1094            0x04, // type = KEEPALIVE
1095        ]);
1096        let mut data = valid_bytes.clone();
1097        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1098        assert!(result.is_ok(), "Valid marker should parse successfully");
1099
1100        // Test that message with invalid marker (all zeros) is handled
1101        // Parser should warn but still process (for MRT compatibility)
1102        let invalid_bytes = Bytes::from(vec![
1103            0x00, 0x00, 0x00, 0x00, // marker (invalid - should be 0xFF)
1104            0x00, 0x00, 0x00, 0x00, // marker
1105            0x00, 0x00, 0x00, 0x00, // marker
1106            0x00, 0x00, 0x00, 0x00, // marker
1107            0x00, 0x13, // length = 19
1108            0x04, // type = KEEPALIVE
1109        ]);
1110        let mut data = invalid_bytes.clone();
1111        // Should still parse (with warning) for compatibility
1112        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1113        assert!(
1114            result.is_ok(),
1115            "Invalid marker should still parse (with warning)"
1116        );
1117    }
1118
1119    #[test]
1120    fn test_attribute_length_overflow_protection() {
1121        // Test that large attribute length values are handled correctly
1122        // without causing overflow issues
1123
1124        // Create a BGP UPDATE message with attribute_length that exceeds available data
1125        let update_bytes = Bytes::from(vec![
1126            0x00, 0x00, // withdrawn length = 0
1127            0xFF,
1128            0xFF, // attribute length = 65535 (largest u16, but not enough data)
1129                  // No actual attribute data follows
1130        ]);
1131
1132        let result = parse_bgp_update_message(update_bytes, false, &AsnLength::Bits16);
1133        assert!(
1134            result.is_err(),
1135            "Should fail when attribute_length exceeds available data"
1136        );
1137        assert!(
1138            matches!(result, Err(ParserError::TruncatedMsg(_))),
1139            "Should fail with TruncatedMsg error"
1140        );
1141
1142        // Test valid attribute length parsing
1143        let valid_update = Bytes::from(vec![
1144            0x00, 0x00, // withdrawn length = 0
1145            0x00,
1146            0x00, // attribute length = 0
1147                  // No attributes, valid empty UPDATE
1148        ]);
1149        let result = parse_bgp_update_message(valid_update, false, &AsnLength::Bits16);
1150        assert!(result.is_ok(), "Should parse valid empty UPDATE");
1151    }
1152
1153    #[test]
1154    fn test_parse_bgp_notification_message() {
1155        let bytes = Bytes::from_static(&[
1156            0x01, // error code
1157            0x02, // error subcode
1158            0x00, 0x00, // data
1159        ]);
1160        let msg = parse_bgp_notification_message(bytes).unwrap();
1161        matches!(
1162            msg.error,
1163            BgpError::MessageHeaderError(MessageHeaderError::BAD_MESSAGE_LENGTH)
1164        );
1165        assert_eq!(msg.data, Bytes::from_static(&[0x00, 0x00]));
1166    }
1167
1168    #[test]
1169    fn test_encode_bgp_notification_messsage() {
1170        let msg = BgpNotificationMessage {
1171            error: BgpError::MessageHeaderError(MessageHeaderError::BAD_MESSAGE_LENGTH),
1172            data: vec![0x00, 0x00],
1173        };
1174        let bytes = msg.encode();
1175        assert_eq!(bytes, Bytes::from_static(&[0x01, 0x02, 0x00, 0x00]));
1176    }
1177
1178    #[test]
1179    fn test_parse_bgp_open_message() {
1180        let bytes = Bytes::from_static(&[
1181            0x04, // version
1182            0x00, 0x01, // asn
1183            0x00, 0xb4, // hold time
1184            0xc0, 0x00, 0x02, 0x01, // sender ip
1185            0x00, // opt params length
1186        ]);
1187        let msg = parse_bgp_open_message(&mut bytes.clone()).unwrap();
1188        assert_eq!(msg.version, 4);
1189        assert_eq!(msg.asn, Asn::new_16bit(1));
1190        assert_eq!(msg.hold_time, 180);
1191        assert_eq!(msg.bgp_identifier, Ipv4Addr::new(192, 0, 2, 1));
1192        assert!(!msg.extended_length);
1193        assert_eq!(msg.opt_params.len(), 0);
1194    }
1195
1196    #[test]
1197    fn test_encode_bgp_open_message() {
1198        let msg = BgpOpenMessage {
1199            version: 4,
1200            asn: Asn::new_16bit(1),
1201            hold_time: 180,
1202            bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
1203            extended_length: false,
1204            opt_params: vec![],
1205        };
1206        let bytes = msg.encode().unwrap();
1207        assert_eq!(
1208            bytes,
1209            Bytes::from_static(&[
1210                0x04, // version
1211                0x00, 0x01, // asn
1212                0x00, 0xb4, // hold time
1213                0xc0, 0x00, 0x02, 0x01, // sender ip
1214                0x00, // opt params length
1215            ])
1216        );
1217    }
1218
1219    #[test]
1220    fn test_bgp_open_wire_fixtures_round_trip_byte_identically() {
1221        // test vectors from sessions with RIS RRC00
1222        let fixtures = [
1223            (
1224                "no optional parameters",
1225                "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001D01",
1226                "048826005A02380BFE00",
1227                0x00,
1228            ),
1229            (
1230                "two capability parameters",
1231                "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF002501",
1232                "045BA0005A66433801080202800002020200",
1233                0x08,
1234            ),
1235            (
1236                "five capability parameters",
1237                "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003901",
1238                "04947100B4CBD0B16E1C02060104000200010202800002020200020246000206410400009471",
1239                0x1C,
1240            ),
1241            (
1242                "one 22-byte capability parameter",
1243                "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003501",
1244                "045BA000F05D9FBB0118021601040001000102004002007841040003215C46004700",
1245                0x18,
1246            ),
1247            (
1248                "one 26-byte capability parameter",
1249                "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003901",
1250                "045BA0012CB901A6321C021A0104000100010200400600780001010041040003167D46004700",
1251                0x1C,
1252            ),
1253        ];
1254
1255        for (name, header, body, expected_opt_params_len) in fixtures {
1256            let wire = Bytes::from(hex::decode(format!("{header}{body}")).unwrap());
1257            assert_eq!(wire[28], expected_opt_params_len, "{name}");
1258
1259            let mut input = wire.clone();
1260            let parsed =
1261                parse_bgp_message(&mut input, false, &AsnLength::Bits16).unwrap_or_else(|error| {
1262                    panic!("failed to parse {name}: {error}");
1263                });
1264            let encoded = parsed.encode(AsnLength::Bits16).unwrap();
1265
1266            assert_eq!(encoded, wire, "{name}");
1267        }
1268    }
1269
1270    #[test]
1271    fn test_bgp_open_encoding_recomputes_parameter_lengths() {
1272        let msg = BgpOpenMessage {
1273            version: 4,
1274            asn: Asn::new_16bit(64512),
1275            hold_time: 90,
1276            bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
1277            extended_length: false,
1278            opt_params: vec![OptParam {
1279                param_type: 254,
1280                param_value: ParamValue::Raw(vec![0xAA, 0xBB, 0xCC]),
1281            }],
1282        };
1283
1284        let encoded = msg.encode().unwrap();
1285
1286        assert_eq!(encoded[9], 5);
1287        assert_eq!(&encoded[10..], &[254, 3, 0xAA, 0xBB, 0xCC]);
1288        let parsed = parse_bgp_open_message(&mut encoded.clone()).unwrap();
1289        assert_eq!(parsed.encode().unwrap(), encoded);
1290    }
1291
1292    #[test]
1293    fn test_bgp_open_encoding_rejects_oversized_add_path_capability() {
1294        use crate::models::capabilities::{AddPathAddressFamily, AddPathSendReceive};
1295
1296        let address_family = AddPathAddressFamily {
1297            afi: Afi::Ipv4,
1298            safi: Safi::Unicast,
1299            send_receive: AddPathSendReceive::SendReceive,
1300        };
1301        let msg = BgpOpenMessage {
1302            version: 4,
1303            asn: Asn::new_16bit(64512),
1304            hold_time: 90,
1305            bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
1306            extended_length: false,
1307            opt_params: vec![OptParam {
1308                param_type: 2,
1309                param_value: ParamValue::Capacities(vec![Capability {
1310                    ty: BgpCapabilityType::ADD_PATH_CAPABILITY,
1311                    value: CapabilityValue::AddPath(AddPathCapability::new(vec![
1312                        address_family;
1313                        64
1314                    ])),
1315                }]),
1316            }],
1317        };
1318
1319        let err = msg.encode().unwrap_err();
1320        assert_eq!(
1321            err,
1322            EncodingError::ValueTooLarge {
1323                field: "BGP capability value length",
1324                actual: 256,
1325                max: 255
1326            }
1327        );
1328    }
1329
1330    #[test]
1331    fn test_bgp_open_forced_extended_parameter_encoding() {
1332        let msg = BgpOpenMessage {
1333            version: 4,
1334            asn: Asn::new_16bit(64512),
1335            hold_time: 90,
1336            bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
1337            extended_length: true,
1338            opt_params: vec![OptParam {
1339                param_type: 254,
1340                param_value: ParamValue::Raw(vec![0xAA, 0xBB]),
1341            }],
1342        };
1343
1344        let encoded = msg.encode().unwrap();
1345
1346        assert_eq!(
1347            &encoded[9..],
1348            &[0xFF, 0xFF, 0x00, 0x05, 254, 0x00, 0x02, 0xAA, 0xBB]
1349        );
1350        let parsed = parse_bgp_open_message(&mut encoded.clone()).unwrap();
1351        assert!(parsed.extended_length);
1352        assert_eq!(parsed.encode().unwrap(), encoded);
1353    }
1354
1355    #[test]
1356    fn test_bgp_open_automatically_uses_extended_parameter_encoding() {
1357        let msg = BgpOpenMessage {
1358            version: 4,
1359            asn: Asn::new_16bit(64512),
1360            hold_time: 90,
1361            bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
1362            extended_length: false,
1363            opt_params: vec![OptParam {
1364                param_type: 254,
1365                param_value: ParamValue::Raw(vec![0xAA; 256]),
1366            }],
1367        };
1368
1369        let encoded = msg.encode().unwrap();
1370
1371        assert_eq!(encoded.len(), 272);
1372        assert_eq!(&encoded[9..16], &[0xFF, 0xFF, 0x01, 0x03, 254, 0x01, 0x00]);
1373        let parsed = parse_bgp_open_message(&mut encoded.clone()).unwrap();
1374        assert!(parsed.extended_length);
1375        assert_eq!(parsed.encode().unwrap(), encoded);
1376    }
1377
1378    #[test]
1379    fn test_encode_bgp_notification_message() {
1380        let bgp_message = BgpMessage::Notification(BgpNotificationMessage {
1381            error: BgpError::MessageHeaderError(MessageHeaderError::BAD_MESSAGE_LENGTH),
1382            data: vec![0x00, 0x00],
1383        });
1384        let bytes = bgp_message.encode(AsnLength::Bits16).unwrap();
1385        // RFC 4271: Marker is 16 bytes of 0xFF
1386        assert_eq!(
1387            bytes,
1388            Bytes::from_static(&[
1389                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // marker (8 bytes)
1390                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // marker (8 bytes)
1391                0x00, 0x17, // length = 23 (16 marker + 2 len + 1 type + 4 msg)
1392                0x03, // type = NOTIFICATION
1393                0x01, 0x02, // error code, subcode
1394                0x00, 0x00 // data
1395            ])
1396        );
1397    }
1398
1399    #[test]
1400    fn test_bgp_message_from_bgp_update_message() {
1401        let msg = BgpMessage::from(BgpUpdateMessage::default());
1402        assert!(matches!(msg, BgpMessage::Update(_)));
1403    }
1404
1405    #[test]
1406    fn test_parse_bgp_open_message_with_extended_next_hop_capability() {
1407        use crate::models::{Afi, Safi};
1408
1409        // BGP OPEN message with Extended Next Hop capability - RFC 8950, Section 3
1410        // Version=4, ASN=65001, HoldTime=180, BGP-ID=192.0.2.1
1411        // One capability: Extended Next Hop (type=5) with two entries:
1412        // 1) IPv4 Unicast (AFI=1, SAFI=1) can use IPv6 NextHop (AFI=2)
1413        // 2) IPv4 MPLS VPN (AFI=1, SAFI=128) can use IPv6 NextHop (AFI=2)
1414        let bytes = Bytes::from(vec![
1415            0x04, // version
1416            0xfd, 0xe9, // asn = 65001
1417            0x00, 0xb4, // hold time = 180
1418            0xc0, 0x00, 0x02, 0x01, // sender ip = 192.0.2.1
1419            0x10, // opt params length = 16
1420            0x02, // param type = 2 (capability)
1421            0x0e, // param length = 14
1422            0x05, // capability type = 5 (Extended Next Hop)
1423            0x0c, // capability length = 12 (2 entries * 6 bytes each)
1424            0x00, 0x01, // NLRI AFI = 1 (IPv4)
1425            0x00, 0x01, // NLRI SAFI = 1 (Unicast)
1426            0x00, 0x02, // NextHop AFI = 2 (IPv6)
1427            0x00, 0x01, // NLRI AFI = 1 (IPv4) - second entry
1428            0x00, 0x80, // NLRI SAFI = 128 (MPLS VPN)
1429            0x00, 0x02, // NextHop AFI = 2 (IPv6)
1430        ]);
1431
1432        let msg = parse_bgp_open_message(&mut bytes.clone()).unwrap();
1433        assert_eq!(msg.version, 4);
1434        assert_eq!(msg.asn, Asn::new_16bit(65001));
1435        assert_eq!(msg.hold_time, 180);
1436        assert_eq!(msg.bgp_identifier, Ipv4Addr::new(192, 0, 2, 1));
1437        assert!(!msg.extended_length);
1438        assert_eq!(msg.opt_params.len(), 1);
1439
1440        // Check the capability
1441        if let ParamValue::Capacities(cap) = &msg.opt_params[0].param_value {
1442            assert_eq!(cap[0].ty, BgpCapabilityType::EXTENDED_NEXT_HOP_ENCODING);
1443
1444            if let CapabilityValue::ExtendedNextHop(enh_cap) = &cap[0].value {
1445                assert_eq!(enh_cap.entries.len(), 2);
1446
1447                // Check first entry: IPv4 Unicast can use IPv6 NextHop
1448                let entry1 = &enh_cap.entries[0];
1449                assert_eq!(entry1.nlri_afi, Afi::Ipv4);
1450                assert_eq!(entry1.nlri_safi, Safi::Unicast);
1451                assert_eq!(entry1.nexthop_afi, Afi::Ipv6);
1452
1453                // Check second entry: IPv4 MPLS VPN can use IPv6 NextHop
1454                let entry2 = &enh_cap.entries[1];
1455                assert_eq!(entry2.nlri_afi, Afi::Ipv4);
1456                assert_eq!(entry2.nlri_safi, Safi::MplsVpn);
1457                assert_eq!(entry2.nexthop_afi, Afi::Ipv6);
1458
1459                // Test functionality
1460                assert!(enh_cap.supports(Afi::Ipv4, Safi::Unicast, Afi::Ipv6));
1461                assert!(enh_cap.supports(Afi::Ipv4, Safi::MplsVpn, Afi::Ipv6));
1462                assert!(!enh_cap.supports(Afi::Ipv4, Safi::Multicast, Afi::Ipv6));
1463            } else {
1464                panic!("Expected ExtendedNextHop capability value");
1465            }
1466        } else {
1467            panic!("Expected capability parameter");
1468        }
1469    }
1470
1471    #[test]
1472    fn test_rfc8654_extended_message_length_validation() {
1473        // Test valid extended UPDATE message (within 65535 limit)
1474        let bytes = Bytes::from_static(&[
1475            0x00, 0x00, 0x00, 0x00, // marker
1476            0x00, 0x00, 0x00, 0x00, // marker
1477            0x00, 0x00, 0x00, 0x00, // marker
1478            0x00, 0x00, 0x00, 0x00, // marker
1479            0x13, 0x00, // length = 4864 (0x1300) (extended message)
1480            0x02, // type = UPDATE
1481            0x00, 0x00, // withdrawn length = 0
1482            0x00,
1483            0x00, // path attribute length = 0
1484                  // No NLRI data needed for this test
1485        ]);
1486        let mut data = bytes.clone();
1487        // This should succeed because UPDATE messages can be extended
1488        assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits16).is_ok());
1489
1490        // Test OPEN message exceeding 4096 bytes (should fail)
1491        let bytes = Bytes::from_static(&[
1492            0x00, 0x00, 0x00, 0x00, // marker
1493            0x00, 0x00, 0x00, 0x00, // marker
1494            0x00, 0x00, 0x00, 0x00, // marker
1495            0x00, 0x00, 0x00, 0x00, // marker
1496            0x13, 0x00, // length = 4864 (0x1300) (exceeds 4096 for OPEN)
1497            0x01, // type = OPEN
1498        ]);
1499        let mut data = bytes.clone();
1500        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1501        assert!(result.is_err());
1502        if let Err(ParserError::ParseError(msg)) = result {
1503            assert!(msg.contains("BGP OPEN message length"));
1504            assert!(msg.contains("4096 bytes"));
1505        }
1506
1507        // Test KEEPALIVE message exceeding 4096 bytes (should fail)
1508        let bytes = Bytes::from_static(&[
1509            0x00, 0x00, 0x00, 0x00, // marker
1510            0x00, 0x00, 0x00, 0x00, // marker
1511            0x00, 0x00, 0x00, 0x00, // marker
1512            0x00, 0x00, 0x00, 0x00, // marker
1513            0x13, 0x00, // length = 4864 (0x1300) (exceeds 4096 for KEEPALIVE)
1514            0x04, // type = KEEPALIVE
1515        ]);
1516        let mut data = bytes.clone();
1517        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1518        assert!(result.is_err());
1519        if let Err(ParserError::ParseError(msg)) = result {
1520            assert!(msg.contains("BGP KEEPALIVE message length"));
1521            assert!(msg.contains("4096 bytes"));
1522        }
1523
1524        // Test message exceeding 65535 bytes (maximum allowed)
1525        let bytes = Bytes::from_static(&[
1526            0x00, 0x00, 0x00, 0x00, // marker
1527            0x00, 0x00, 0x00, 0x00, // marker
1528            0x00, 0x00, 0x00, 0x00, // marker
1529            0x00, 0x00, 0x00, 0x00, // marker
1530            0xFF, 0xFF, // length = 65535 (0xFFFF) (maximum allowed)
1531            0x02, // type = UPDATE
1532        ]);
1533        let mut data = bytes.clone();
1534        // This might fail due to insufficient data, but should not fail on length validation
1535        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1536        if let Err(ParserError::ParseError(msg)) = result {
1537            // Should not be a length validation error
1538            assert!(!msg.contains("invalid BGP message length"));
1539        }
1540    }
1541
1542    #[test]
1543    fn test_bgp_extended_message_capability_parsing() {
1544        use crate::models::CapabilityValue;
1545
1546        // Test BGP OPEN message with Extended Message capability (capability code 6)
1547        let bytes = Bytes::from(vec![
1548            0x04, // version
1549            0x00, 0x01, // asn
1550            0x00, 0xb4, // hold time
1551            0xc0, 0x00, 0x02, 0x01, // sender ip
1552            0x04, // opt params length = 4
1553            0x02, // param type = 2 (capability)
1554            0x02, // param length = 2
1555            0x06, // capability type = 6 (Extended Message)
1556            0x00, // capability length = 0 (no parameters)
1557        ]);
1558
1559        let msg = parse_bgp_open_message(&mut bytes.clone()).unwrap();
1560        assert_eq!(msg.version, 4);
1561        assert_eq!(msg.asn, Asn::new_16bit(1));
1562        assert_eq!(msg.opt_params.len(), 1);
1563
1564        // Check that we have the extended message capability
1565        if let ParamValue::Capacities(cap) = &msg.opt_params[0].param_value {
1566            assert_eq!(cap[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
1567            if let CapabilityValue::BgpExtendedMessage(_) = &cap[0].value {
1568                // Extended Message capability should have no parameters
1569            } else {
1570                panic!("Expected BgpExtendedMessage capability value");
1571            }
1572        } else {
1573            panic!("Expected capability parameter");
1574        }
1575    }
1576
1577    #[test]
1578    fn test_rfc8654_edge_cases() {
1579        // Test NOTIFICATION message with extended length (should be allowed)
1580        let bytes = Bytes::from_static(&[
1581            0x00, 0x00, 0x00, 0x00, // marker
1582            0x00, 0x00, 0x00, 0x00, // marker
1583            0x00, 0x00, 0x00, 0x00, // marker
1584            0x00, 0x00, 0x00, 0x00, // marker
1585            0x20, 0x00, // length = 8192 (extended NOTIFICATION message)
1586            0x03, // type = NOTIFICATION
1587            0x06, // error code (Cease)
1588            0x00, // error subcode
1589                  // Additional data would go here
1590        ]);
1591        let mut data = bytes.clone();
1592        // This should succeed because NOTIFICATION messages can be extended
1593        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1594        // May fail due to insufficient data, but not due to length validation
1595        if let Err(ParserError::ParseError(msg)) = result {
1596            assert!(!msg.contains("invalid BGP message length"));
1597            assert!(!msg.contains("exceeds maximum allowed 4096 bytes"));
1598        }
1599
1600        // Test message exactly at 4096 bytes for OPEN (should be allowed)
1601        let open_data = vec![
1602            0x00, 0x00, 0x00, 0x00, // marker
1603            0x00, 0x00, 0x00, 0x00, // marker
1604            0x00, 0x00, 0x00, 0x00, // marker
1605            0x00, 0x00, 0x00, 0x00, // marker
1606            0x10, 0x00, // length = 4096 (exactly at limit for OPEN)
1607            0x01, // type = OPEN
1608        ];
1609        let bytes = Bytes::from(open_data);
1610        let mut data = bytes.clone();
1611        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1612        // Should not fail on length validation (may fail on parsing due to insufficient data)
1613        if let Err(ParserError::ParseError(msg)) = result {
1614            assert!(!msg.contains("exceeds maximum allowed 4096 bytes"));
1615        }
1616
1617        // Test message exactly at 65535 bytes for UPDATE (should be allowed)
1618        let bytes = Bytes::from_static(&[
1619            0x00, 0x00, 0x00, 0x00, // marker
1620            0x00, 0x00, 0x00, 0x00, // marker
1621            0x00, 0x00, 0x00, 0x00, // marker
1622            0x00, 0x00, 0x00, 0x00, // marker
1623            0xFF, 0xFF, // length = 65535 (0xFFFF) (maximum allowed)
1624            0x02, // type = UPDATE
1625        ]);
1626        let mut data = bytes.clone();
1627        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1628        // Should not fail on length validation
1629        if let Err(ParserError::ParseError(msg)) = result {
1630            assert!(!msg.contains("invalid BGP message length"));
1631        }
1632    }
1633
1634    #[test]
1635    fn test_rfc8654_capability_encoding_path() {
1636        use crate::models::capabilities::BgpExtendedMessageCapability;
1637
1638        // Test that the encoding path for BgpExtendedMessage capability is covered
1639        // This specifically tests the line: CapabilityValue::BgpExtendedMessage(bem) => bem.encode().unwrap()
1640        let capability_value =
1641            CapabilityValue::BgpExtendedMessage(BgpExtendedMessageCapability::new());
1642        let capability = Capability {
1643            ty: BgpCapabilityType::BGP_EXTENDED_MESSAGE,
1644            value: capability_value,
1645        };
1646
1647        let opt_param = OptParam {
1648            param_type: 2, // capability
1649            param_value: ParamValue::Capacities(vec![capability]),
1650        };
1651
1652        let msg = BgpOpenMessage {
1653            version: 4,
1654            asn: Asn::new_16bit(65001),
1655            hold_time: 180,
1656            bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
1657            extended_length: false,
1658            opt_params: vec![opt_param],
1659        };
1660
1661        // This will exercise the encoding path we need to test
1662        let encoded = msg.encode().unwrap();
1663        assert!(!encoded.is_empty());
1664
1665        // Verify we can parse it back (exercises the parsing path too)
1666        let parsed = parse_bgp_open_message(&mut encoded.clone()).unwrap();
1667        assert_eq!(parsed.opt_params.len(), 1);
1668        if let ParamValue::Capacities(cap) = &parsed.opt_params[0].param_value {
1669            assert_eq!(cap[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
1670        }
1671    }
1672
1673    #[test]
1674    fn test_rfc8654_error_message_formatting() {
1675        // Test the error message formatting paths that include message type names
1676        // This tests the match arms for OPEN and KEEPALIVE in error messages
1677
1678        // Test OPEN message error path
1679        let bytes = Bytes::from_static(&[
1680            0x00, 0x00, 0x00, 0x00, // marker
1681            0x00, 0x00, 0x00, 0x00, // marker
1682            0x00, 0x00, 0x00, 0x00, // marker
1683            0x00, 0x00, 0x00, 0x00, // marker
1684            0x20, 0x01, // length = 8193 (exceeds 4096 for OPEN)
1685            0x01, // type = OPEN
1686        ]);
1687        let mut data = bytes.clone();
1688        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1689        assert!(result.is_err());
1690        if let Err(ParserError::ParseError(msg)) = result {
1691            assert!(msg.contains("BGP OPEN message length"));
1692            assert!(msg.contains("exceeds maximum allowed 4096 bytes"));
1693        }
1694
1695        // Test KEEPALIVE message error path
1696        let bytes = Bytes::from_static(&[
1697            0x00, 0x00, 0x00, 0x00, // marker
1698            0x00, 0x00, 0x00, 0x00, // marker
1699            0x00, 0x00, 0x00, 0x00, // marker
1700            0x00, 0x00, 0x00, 0x00, // marker
1701            0x20, 0x01, // length = 8193 (exceeds 4096 for KEEPALIVE)
1702            0x04, // type = KEEPALIVE
1703        ]);
1704        let mut data = bytes.clone();
1705        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1706        assert!(result.is_err());
1707        if let Err(ParserError::ParseError(msg)) = result {
1708            assert!(msg.contains("BGP KEEPALIVE message length"));
1709            assert!(msg.contains("exceeds maximum allowed 4096 bytes"));
1710        }
1711    }
1712
1713    #[test]
1714    fn test_encode_bgp_open_message_with_extended_message_capability() {
1715        use crate::models::capabilities::BgpExtendedMessageCapability;
1716
1717        // Create Extended Message capability
1718        let extended_msg_capability = BgpExtendedMessageCapability::new();
1719
1720        let msg = BgpOpenMessage {
1721            version: 4,
1722            asn: Asn::new_16bit(65001),
1723            hold_time: 180,
1724            bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
1725            extended_length: false,
1726            opt_params: vec![OptParam {
1727                param_type: 2, // capability
1728                param_value: ParamValue::Capacities(vec![Capability {
1729                    ty: BgpCapabilityType::BGP_EXTENDED_MESSAGE,
1730                    value: CapabilityValue::BgpExtendedMessage(extended_msg_capability),
1731                }]),
1732            }],
1733        };
1734
1735        let encoded = msg.encode().unwrap();
1736
1737        // Parse the encoded message back and verify it matches
1738        let parsed = parse_bgp_open_message(&mut encoded.clone()).unwrap();
1739        assert_eq!(parsed.version, msg.version);
1740        assert_eq!(parsed.asn, msg.asn);
1741        assert_eq!(parsed.hold_time, msg.hold_time);
1742        assert_eq!(parsed.bgp_identifier, msg.bgp_identifier);
1743        assert_eq!(parsed.opt_params.len(), 1);
1744
1745        // Verify the capability was encoded and parsed correctly
1746        if let ParamValue::Capacities(cap) = &parsed.opt_params[0].param_value {
1747            assert_eq!(cap[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
1748            if let CapabilityValue::BgpExtendedMessage(_) = &cap[0].value {
1749                // Extended Message capability should have no parameters
1750            } else {
1751                panic!("Expected BgpExtendedMessage capability value after round trip");
1752            }
1753        } else {
1754            panic!("Expected capability parameter after round trip");
1755        }
1756    }
1757
1758    #[test]
1759    fn test_encode_bgp_open_message_with_extended_next_hop_capability() {
1760        use crate::models::capabilities::{ExtendedNextHopCapability, ExtendedNextHopEntry};
1761        use crate::models::{Afi, Safi};
1762
1763        // Create Extended Next Hop capability
1764        let entries = vec![
1765            ExtendedNextHopEntry {
1766                nlri_afi: Afi::Ipv4,
1767                nlri_safi: Safi::Unicast,
1768                nexthop_afi: Afi::Ipv6,
1769            },
1770            ExtendedNextHopEntry {
1771                nlri_afi: Afi::Ipv4,
1772                nlri_safi: Safi::MplsVpn,
1773                nexthop_afi: Afi::Ipv6,
1774            },
1775        ];
1776        let enh_capability = ExtendedNextHopCapability::new(entries);
1777
1778        let msg = BgpOpenMessage {
1779            version: 4,
1780            asn: Asn::new_16bit(65001),
1781            hold_time: 180,
1782            bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
1783            extended_length: false,
1784            opt_params: vec![OptParam {
1785                param_type: 2, // capability
1786                param_value: ParamValue::Capacities(vec![Capability {
1787                    ty: BgpCapabilityType::EXTENDED_NEXT_HOP_ENCODING,
1788                    value: CapabilityValue::ExtendedNextHop(enh_capability),
1789                }]),
1790            }],
1791        };
1792
1793        let encoded = msg.encode().unwrap();
1794
1795        // Parse the encoded message back and verify it matches
1796        let parsed = parse_bgp_open_message(&mut encoded.clone()).unwrap();
1797        assert_eq!(parsed.version, msg.version);
1798        assert_eq!(parsed.asn, msg.asn);
1799        assert_eq!(parsed.hold_time, msg.hold_time);
1800        assert_eq!(parsed.bgp_identifier, msg.bgp_identifier);
1801        assert_eq!(parsed.extended_length, msg.extended_length);
1802        assert_eq!(parsed.opt_params.len(), 1);
1803
1804        // Verify the capability was encoded and parsed correctly
1805        if let ParamValue::Capacities(cap) = &parsed.opt_params[0].param_value {
1806            assert_eq!(cap[0].ty, BgpCapabilityType::EXTENDED_NEXT_HOP_ENCODING);
1807            if let CapabilityValue::ExtendedNextHop(enh_cap) = &cap[0].value {
1808                assert_eq!(enh_cap.entries.len(), 2);
1809                assert!(enh_cap.supports(Afi::Ipv4, Safi::Unicast, Afi::Ipv6));
1810                assert!(enh_cap.supports(Afi::Ipv4, Safi::MplsVpn, Afi::Ipv6));
1811            } else {
1812                panic!("Expected ExtendedNextHop capability value after round trip");
1813            }
1814        } else {
1815            panic!("Expected capability parameter after round trip");
1816        }
1817    }
1818
1819    #[test]
1820    fn test_parse_bgp_open_message_with_multiple_capabilities() {
1821        // Create a BGP OPEN message with multiple capabilities in a single optional parameter
1822        // This tests RFC 5492 support for multiple capabilities per parameter
1823
1824        // Build capabilities: Extended Message, Route Refresh, and 4-octet AS
1825        let extended_msg_cap = Capability {
1826            ty: BgpCapabilityType::BGP_EXTENDED_MESSAGE,
1827            value: CapabilityValue::BgpExtendedMessage(BgpExtendedMessageCapability {}),
1828        };
1829
1830        let route_refresh_cap = Capability {
1831            ty: BgpCapabilityType::ROUTE_REFRESH_CAPABILITY_FOR_BGP_4,
1832            value: CapabilityValue::RouteRefresh(RouteRefreshCapability {}),
1833        };
1834
1835        let four_octet_as_cap = Capability {
1836            ty: BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY,
1837            value: CapabilityValue::FourOctetAs(FourOctetAsCapability { asn: 65536 }),
1838        };
1839
1840        // Create OPEN message with all three capabilities in one parameter
1841        let msg = BgpOpenMessage {
1842            version: 4,
1843            asn: Asn::new_32bit(65000),
1844            hold_time: 180,
1845            bgp_identifier: "10.0.0.1".parse().unwrap(),
1846            extended_length: false,
1847            opt_params: vec![OptParam {
1848                param_type: 2, // capability
1849                param_value: ParamValue::Capacities(vec![
1850                    extended_msg_cap,
1851                    route_refresh_cap,
1852                    four_octet_as_cap,
1853                ]),
1854            }],
1855        };
1856
1857        // Encode the message
1858        let encoded = msg.encode().unwrap();
1859
1860        // Parse it back
1861        let mut encoded_bytes = encoded.clone();
1862        let parsed = parse_bgp_open_message(&mut encoded_bytes).unwrap();
1863
1864        // Verify basic fields
1865        assert_eq!(parsed.version, 4);
1866        assert_eq!(parsed.asn, Asn::new_32bit(65000));
1867        assert_eq!(parsed.hold_time, 180);
1868        assert_eq!(
1869            parsed.bgp_identifier,
1870            "10.0.0.1".parse::<std::net::Ipv4Addr>().unwrap()
1871        );
1872        assert_eq!(parsed.opt_params.len(), 1);
1873
1874        // Verify we have all three capabilities
1875        if let ParamValue::Capacities(caps) = &parsed.opt_params[0].param_value {
1876            assert_eq!(caps.len(), 3, "Should have 3 capabilities");
1877
1878            // Check first capability: Extended Message
1879            assert_eq!(caps[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
1880            assert!(matches!(
1881                caps[0].value,
1882                CapabilityValue::BgpExtendedMessage(_)
1883            ));
1884
1885            // Check second capability: Route Refresh
1886            assert_eq!(
1887                caps[1].ty,
1888                BgpCapabilityType::ROUTE_REFRESH_CAPABILITY_FOR_BGP_4
1889            );
1890            assert!(matches!(caps[1].value, CapabilityValue::RouteRefresh(_)));
1891
1892            // Check third capability: 4-octet AS
1893            assert_eq!(
1894                caps[2].ty,
1895                BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY
1896            );
1897            if let CapabilityValue::FourOctetAs(foa) = &caps[2].value {
1898                assert_eq!(foa.asn, 65536);
1899            } else {
1900                panic!("Expected FourOctetAs capability value");
1901            }
1902        } else {
1903            panic!("Expected Capacities parameter");
1904        }
1905    }
1906
1907    #[test]
1908    fn test_parse_bgp_open_message_with_multiple_capability_parameters() {
1909        // Test parsing OPEN message with multiple optional parameters, each containing capabilities
1910        // This is less common but still valid per RFC 5492
1911
1912        let msg = BgpOpenMessage {
1913            version: 4,
1914            asn: Asn::new_32bit(65001),
1915            hold_time: 90,
1916            bgp_identifier: "192.168.1.1".parse().unwrap(),
1917            extended_length: false,
1918            opt_params: vec![
1919                OptParam {
1920                    param_type: 2, // capability
1921                    param_value: ParamValue::Capacities(vec![Capability {
1922                        ty: BgpCapabilityType::BGP_EXTENDED_MESSAGE,
1923                        value: CapabilityValue::BgpExtendedMessage(BgpExtendedMessageCapability {}),
1924                    }]),
1925                },
1926                OptParam {
1927                    param_type: 2, // capability
1928                    param_value: ParamValue::Capacities(vec![Capability {
1929                        ty: BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY,
1930                        value: CapabilityValue::FourOctetAs(FourOctetAsCapability {
1931                            asn: 4200000000,
1932                        }),
1933                    }]),
1934                },
1935            ],
1936        };
1937
1938        // Encode and parse back
1939        let encoded = msg.encode().unwrap();
1940        let mut encoded_bytes = encoded.clone();
1941        let parsed = parse_bgp_open_message(&mut encoded_bytes).unwrap();
1942
1943        // Verify we have 2 optional parameters
1944        assert_eq!(parsed.opt_params.len(), 2);
1945
1946        // Check first parameter
1947        if let ParamValue::Capacities(caps) = &parsed.opt_params[0].param_value {
1948            assert_eq!(caps.len(), 1);
1949            assert_eq!(caps[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
1950        } else {
1951            panic!("Expected Capacities in first parameter");
1952        }
1953
1954        // Check second parameter
1955        if let ParamValue::Capacities(caps) = &parsed.opt_params[1].param_value {
1956            assert_eq!(caps.len(), 1);
1957            assert_eq!(
1958                caps[0].ty,
1959                BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY
1960            );
1961            if let CapabilityValue::FourOctetAs(foa) = &caps[0].value {
1962                assert_eq!(foa.asn, 4200000000);
1963            }
1964        } else {
1965            panic!("Expected Capacities in second parameter");
1966        }
1967    }
1968}