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::error::ParserError;
7use crate::models::capabilities::{
8    AddPathCapability, BgpCapabilityType, BgpExtendedMessageCapability, BgpRoleCapability,
9    ExtendedNextHopCapability, FourOctetAsCapability, GracefulRestartCapability,
10    MultiprotocolExtensionsCapability, RouteRefreshCapability,
11};
12use crate::models::error::BgpError;
13use crate::parser::bgp::attributes::parse_attributes;
14use crate::parser::{encode_nlri_prefixes, parse_nlri_list, ReadUtils};
15use log::warn;
16use zerocopy::big_endian::{U16, U32};
17use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
18
19/// On-wire BGP OPEN fixed header layout (10 bytes, network byte order).
20#[derive(IntoBytes, FromBytes, KnownLayout, Immutable)]
21#[repr(C)]
22struct RawBgpOpenHeader {
23    version: u8,
24    asn: U16,
25    hold_time: U16,
26    bgp_identifier: U32,
27    opt_params_len: u8,
28}
29
30const _: () = assert!(size_of::<RawBgpOpenHeader>() == 10);
31
32/// BGP message
33///
34/// Format:
35/// ```text
36/// 0                   1                   2                   3
37/// 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
38/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
39/// |                                                               |
40/// +                                                               +
41/// |                                                               |
42/// +                                                               +
43/// |                           Marker                              |
44/// +                                                               +
45/// |                                                               |
46/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47/// |          Length               |      Type     |
48/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
49/// ```
50pub fn parse_bgp_message(
51    data: &mut Bytes,
52    add_path: bool,
53    asn_len: &AsnLength,
54) -> Result<BgpMessage, ParserError> {
55    let total_size = data.len();
56    data.has_n_remaining(19)?;
57
58    // Read and validate BGP marker (RFC 4271: 16 bytes of 0xFF)
59    let mut marker = [0u8; 16];
60    data.copy_to_slice(&mut marker);
61    if marker != [0xFF; 16] {
62        // Log warning for invalid marker but continue processing
63        // Some implementations may use non-standard markers in MRT dumps
64        warn!("BGP message marker is not all 0xFF bytes (invalid per RFC 4271)");
65    }
66
67    /*
68    This 2-octet unsigned integer indicates the total length of the
69    message, including the header in octets.  Thus, it allows one
70    to locate the (Marker field of the) next message in the TCP
71    stream.  The value of the Length field MUST always be at least
72    19 and no greater than 4096, and MAY be further constrained,
73    depending on the message type.  "padding" of extra data after
74    the message is not allowed.  Therefore, the Length field MUST
75    have the smallest value required, given the rest of the
76    message.
77    */
78    let length = data.read_u16()?;
79
80    // Validate message length according to RFC 8654
81    // For now, we allow extended messages for all message types except when we know
82    // for certain that extended messages are not supported.
83    // RFC 8654: Extended messages up to 65535 bytes are allowed for all message types
84    // except OPEN and KEEPALIVE (which remain limited to 4096 bytes).
85    // However, since we're parsing MRT data without session context, we'll be permissive.
86    let max_length = 65535; // RFC 8654 maximum
87    if !(19..=max_length).contains(&length) {
88        return Err(ParserError::ParseError(format!(
89            "invalid BGP message length {length}"
90        )));
91    }
92
93    // Validate length >= 19 before any arithmetic to prevent underflow
94    let length_usize = length as usize;
95    let bgp_msg_length = if length_usize > total_size {
96        total_size.saturating_sub(19)
97    } else {
98        length_usize.saturating_sub(19)
99    };
100
101    let msg_type: BgpMessageType = match BgpMessageType::try_from(data.read_u8()?) {
102        Ok(t) => t,
103        Err(_) => {
104            return Err(ParserError::ParseError(
105                "Unknown BGP Message Type".to_string(),
106            ))
107        }
108    };
109
110    // Additional validation for OPEN and KEEPALIVE messages per RFC 8654
111    // These message types cannot exceed 4096 bytes even with extended message capability
112    match msg_type {
113        BgpMessageType::OPEN | BgpMessageType::KEEPALIVE => {
114            if length > 4096 {
115                return Err(ParserError::ParseError(format!(
116                    "BGP {} message length {} exceeds maximum allowed 4096 bytes (RFC 8654)",
117                    match msg_type {
118                        BgpMessageType::OPEN => "OPEN",
119                        BgpMessageType::KEEPALIVE => "KEEPALIVE",
120                        _ => unreachable!(),
121                    },
122                    length
123                )));
124            }
125        }
126        BgpMessageType::UPDATE | BgpMessageType::NOTIFICATION => {
127            // These can be extended messages up to 65535 bytes when capability is negotiated
128            // Since we're parsing MRT data, we allow extended lengths
129        }
130    }
131
132    if data.remaining() != bgp_msg_length {
133        warn!(
134            "BGP message length {} does not match the actual length {} (parsing BGP message)",
135            bgp_msg_length,
136            data.remaining()
137        );
138    }
139    data.has_n_remaining(bgp_msg_length)?;
140    let mut msg_data = data.split_to(bgp_msg_length);
141
142    Ok(match msg_type {
143        BgpMessageType::OPEN => BgpMessage::Open(parse_bgp_open_message(&mut msg_data)?),
144        BgpMessageType::UPDATE => {
145            BgpMessage::Update(parse_bgp_update_message(msg_data, add_path, asn_len)?)
146        }
147        BgpMessageType::NOTIFICATION => {
148            BgpMessage::Notification(parse_bgp_notification_message(msg_data)?)
149        }
150        BgpMessageType::KEEPALIVE => BgpMessage::KeepAlive,
151    })
152}
153
154/// Parse BGP NOTIFICATION message.
155///
156/// The BGP NOTIFICATION messages contains BGP error codes received from a connected BGP router. The
157/// error code is parsed into [BgpError] data structure and any unknown codes will produce warning
158/// messages, but not critical errors.
159///
160pub fn parse_bgp_notification_message(
161    mut input: Bytes,
162) -> Result<BgpNotificationMessage, ParserError> {
163    let error_code = input.read_u8()?;
164    let error_subcode = input.read_u8()?;
165
166    Ok(BgpNotificationMessage {
167        error: BgpError::new(error_code, error_subcode),
168        data: input.read_n_bytes(input.len())?,
169    })
170}
171
172impl BgpNotificationMessage {
173    pub fn encode(&self) -> Bytes {
174        let mut buf = BytesMut::new();
175        let (code, subcode) = self.error.get_codes();
176        buf.put_u8(code);
177        buf.put_u8(subcode);
178        buf.put_slice(&self.data);
179        buf.freeze()
180    }
181}
182
183/// Parse BGP OPEN message.
184///
185/// The parsing of BGP OPEN message also includes decoding the BGP capabilities.
186///
187/// RFC 4271: <https://datatracker.ietf.org/doc/html/rfc4271>
188/// ```text
189///       0                   1                   2                   3
190///       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
191///       +-+-+-+-+-+-+-+-+
192///       |    Version    |
193///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
194///       |     My Autonomous System      |
195///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
196///       |           Hold Time           |
197///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
198///       |                         BGP Identifier                        |
199///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
200///       | Opt Parm Len  |
201///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
202///       |                                                               |
203///       |             Optional Parameters (variable)                    |
204///       |                                                               |
205///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
206///
207///       0                   1
208///       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
209///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...
210///       |  Parm. Type   | Parm. Length  |  Parameter Value (variable)
211///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...
212/// ```
213pub fn parse_bgp_open_message(input: &mut Bytes) -> Result<BgpOpenMessage, ParserError> {
214    input.has_n_remaining(10)?;
215    let mut header_bytes = [0u8; 10];
216    input.copy_to_slice(&mut header_bytes);
217    // Single bounds check via zerocopy instead of five sequential cursor reads.
218    let raw = RawBgpOpenHeader::ref_from_bytes(&header_bytes)
219        .expect("header_bytes is exactly 10 bytes with no alignment requirement");
220
221    let version = raw.version;
222    let asn = Asn::new_16bit(raw.asn.get());
223    let hold_time = raw.hold_time.get();
224    let bgp_identifier = Ipv4Addr::from(raw.bgp_identifier.get());
225    let mut opt_params_len: u16 = raw.opt_params_len as u16;
226
227    let mut extended_length = false;
228    let mut first = true;
229
230    let mut params: Vec<OptParam> = vec![];
231    while input.remaining() >= 2 {
232        let mut param_type = input.read_u8()?;
233        if first {
234            if opt_params_len == 0 && param_type == 255 {
235                return Err(ParserError::ParseError(
236                    "RFC 9072 violation: Non-Extended Optional Parameters Length must not be 0 when using extended format".to_string()
237                ));
238            }
239            // first parameter, check if it is extended length message
240            if opt_params_len != 0 && param_type == 255 {
241                // RFC 9072: https://datatracker.ietf.org/doc/rfc9072/
242                //
243                // 0                   1                   2                   3
244                // 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
245                //     +-+-+-+-+-+-+-+-+
246                //     |    Version    |
247                //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
248                //     |     My Autonomous System      |
249                //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
250                //     |           Hold Time           |
251                //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
252                //     |                         BGP Identifier                        |
253                //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
254                //     |Non-Ext OP Len.|Non-Ext OP Type|  Extended Opt. Parm. Length   |
255                //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
256                //     |                                                               |
257                //     |             Optional Parameters (variable)                    |
258                //     |                                                               |
259                //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
260                //
261                //         Figure 1: Extended Encoding OPEN Format
262                extended_length = true;
263                opt_params_len = input.read_u16()?;
264                if opt_params_len == 0 {
265                    break;
266                }
267                // let pos_end = input.position() + opt_params_len as u64;
268                if input.remaining() != opt_params_len as usize {
269                    warn!(
270                    "BGP open message length {} does not match the actual length {} (parsing BGP OPEN message)",
271                    opt_params_len,
272                    input.remaining()
273                );
274                }
275
276                param_type = input.read_u8()?;
277            }
278            first = false;
279        }
280        // reaching here means all the remain params are regular non-extended-length parameters
281
282        let param_len = match extended_length {
283            true => input.read_u16()?,
284            false => input.read_u8()? as u16,
285        };
286
287        // https://tools.ietf.org/html/rfc3392
288        // https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-11
289
290        let param_value = match param_type {
291            2 => {
292                let mut capacities = vec![];
293
294                // Split off only the bytes for this parameter to avoid consuming other parameters
295                input.has_n_remaining(param_len as usize)?;
296                let mut param_data = input.split_to(param_len as usize);
297
298                while param_data.remaining() >= 2 {
299                    // capability codes:
300                    // https://www.iana.org/assignments/capability-codes/capability-codes.xhtml#capability-codes-2
301                    let code = param_data.read_u8()?;
302                    let len = param_data.read_u8()? as u16; // Capability length is ALWAYS 1 byte per RFC 5492
303
304                    let capability_data = param_data.read_n_bytes(len as usize)?;
305                    let capability_type = BgpCapabilityType::from(code);
306
307                    // Parse specific capability types with fallback to raw bytes
308                    macro_rules! parse_capability {
309                        ($parser:path, $variant:ident) => {
310                            match $parser(Bytes::from(capability_data.clone())) {
311                                Ok(parsed) => CapabilityValue::$variant(parsed),
312                                Err(_) => CapabilityValue::Raw(capability_data),
313                            }
314                        };
315                    }
316
317                    let capability_value = match capability_type {
318                        BgpCapabilityType::MULTIPROTOCOL_EXTENSIONS_FOR_BGP_4 => {
319                            parse_capability!(
320                                MultiprotocolExtensionsCapability::parse,
321                                MultiprotocolExtensions
322                            )
323                        }
324                        BgpCapabilityType::ROUTE_REFRESH_CAPABILITY_FOR_BGP_4 => {
325                            parse_capability!(RouteRefreshCapability::parse, RouteRefresh)
326                        }
327                        BgpCapabilityType::EXTENDED_NEXT_HOP_ENCODING => {
328                            parse_capability!(ExtendedNextHopCapability::parse, ExtendedNextHop)
329                        }
330                        BgpCapabilityType::GRACEFUL_RESTART_CAPABILITY => {
331                            parse_capability!(GracefulRestartCapability::parse, GracefulRestart)
332                        }
333                        BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY => {
334                            parse_capability!(FourOctetAsCapability::parse, FourOctetAs)
335                        }
336                        BgpCapabilityType::ADD_PATH_CAPABILITY => {
337                            parse_capability!(AddPathCapability::parse, AddPath)
338                        }
339                        BgpCapabilityType::BGP_ROLE => {
340                            parse_capability!(BgpRoleCapability::parse, BgpRole)
341                        }
342                        BgpCapabilityType::BGP_EXTENDED_MESSAGE => {
343                            parse_capability!(
344                                BgpExtendedMessageCapability::parse,
345                                BgpExtendedMessage
346                            )
347                        }
348                        _ => CapabilityValue::Raw(capability_data),
349                    };
350
351                    capacities.push(Capability {
352                        ty: capability_type,
353                        value: capability_value,
354                    });
355                }
356
357                ParamValue::Capacities(capacities)
358            }
359            _ => {
360                // unsupported param, read as raw bytes
361                let bytes = input.read_n_bytes(param_len as usize)?;
362                ParamValue::Raw(bytes)
363            }
364        };
365        params.push(OptParam {
366            param_type,
367            param_len,
368            param_value,
369        });
370    }
371
372    Ok(BgpOpenMessage {
373        version,
374        asn,
375        hold_time,
376        bgp_identifier,
377        extended_length,
378        opt_params: params,
379    })
380}
381
382impl BgpOpenMessage {
383    pub fn encode(&self) -> Bytes {
384        let mut buf = BytesMut::new();
385        let raw_header = RawBgpOpenHeader {
386            version: self.version,
387            asn: U16::new(self.asn.into()),
388            hold_time: U16::new(self.hold_time),
389            bgp_identifier: U32::new(u32::from(self.bgp_identifier)),
390            opt_params_len: self.opt_params.len() as u8,
391        };
392        buf.extend_from_slice(raw_header.as_bytes());
393        for param in &self.opt_params {
394            buf.put_u8(param.param_type);
395            buf.put_u8(param.param_len as u8);
396            match &param.param_value {
397                ParamValue::Capacities(capacities) => {
398                    for cap in capacities {
399                        buf.put_u8(cap.ty.into());
400                        let encoded_value = match &cap.value {
401                            CapabilityValue::MultiprotocolExtensions(mp) => mp.encode(),
402                            CapabilityValue::RouteRefresh(rr) => rr.encode(),
403                            CapabilityValue::ExtendedNextHop(enh) => enh.encode(),
404                            CapabilityValue::GracefulRestart(gr) => gr.encode(),
405                            CapabilityValue::FourOctetAs(foa) => foa.encode(),
406                            CapabilityValue::AddPath(ap) => ap.encode(),
407                            CapabilityValue::BgpRole(br) => br.encode(),
408                            CapabilityValue::BgpExtendedMessage(bem) => bem.encode(),
409                            CapabilityValue::Raw(raw) => Bytes::from(raw.clone()),
410                        };
411                        buf.put_u8(encoded_value.len() as u8);
412                        buf.extend(&encoded_value);
413                    }
414                }
415                ParamValue::Raw(bytes) => {
416                    buf.extend(bytes);
417                }
418            }
419        }
420        buf.freeze()
421    }
422}
423
424/// read nlri portion of a bgp update message.
425fn read_nlri(
426    mut input: Bytes,
427    afi: &Afi,
428    add_path: bool,
429) -> Result<Vec<NetworkPrefix>, ParserError> {
430    let length = input.len();
431    if length == 0 {
432        return Ok(vec![]);
433    }
434    if length == 1 {
435        // 1 byte does not make sense
436        warn!("seeing strange one-byte NLRI field (parsing NLRI in BGP UPDATE message)");
437        input.advance(1); // skip the byte
438        return Ok(vec![]);
439    }
440
441    parse_nlri_list(input, add_path, afi)
442}
443
444/// read bgp update message.
445///
446/// RFC: <https://tools.ietf.org/html/rfc4271#section-4.3>
447pub fn parse_bgp_update_message(
448    mut input: Bytes,
449    add_path: bool,
450    asn_len: &AsnLength,
451) -> Result<BgpUpdateMessage, ParserError> {
452    // NOTE: AFI for routes outside attributes are IPv4 ONLY.
453    let afi = Afi::Ipv4;
454
455    // parse withdrawn prefixes NLRI
456    let withdrawn_bytes_length_raw = input.read_u16()?;
457    let withdrawn_bytes_length = withdrawn_bytes_length_raw as usize;
458    input.has_n_remaining(withdrawn_bytes_length)?;
459    let withdrawn_bytes = input.split_to(withdrawn_bytes_length);
460    let withdrawn_prefixes = read_nlri(withdrawn_bytes, &afi, add_path)?;
461
462    // parse attributes
463    let attribute_length_raw = input.read_u16()?;
464    // Defensive check: ensure attribute_length fits in usize
465    // u16 to usize conversion is always safe on 32/64-bit platforms,
466    // but this check ensures safety on all architectures
467    let attribute_length = attribute_length_raw as usize;
468
469    input.has_n_remaining(attribute_length)?;
470    let attr_data_slice = input.split_to(attribute_length);
471    let attributes = parse_attributes(attr_data_slice, asn_len, add_path, None, None, None)?;
472
473    // parse announced prefixes nlri.
474    // the remaining bytes are announced prefixes.
475    let announced_prefixes = read_nlri(input, &afi, add_path)?;
476
477    Ok(BgpUpdateMessage {
478        withdrawn_prefixes,
479        attributes,
480        announced_prefixes,
481    })
482}
483
484impl BgpUpdateMessage {
485    pub fn encode(&self, asn_len: AsnLength) -> Bytes {
486        let mut bytes = BytesMut::new();
487
488        // withdrawn prefixes
489        let withdrawn_bytes = encode_nlri_prefixes(&self.withdrawn_prefixes);
490        bytes.put_u16(withdrawn_bytes.len() as u16);
491        bytes.put_slice(&withdrawn_bytes);
492
493        // attributes
494        let attr_bytes = self.attributes.encode(asn_len);
495
496        bytes.put_u16(attr_bytes.len() as u16);
497        bytes.put_slice(&attr_bytes);
498
499        bytes.extend(encode_nlri_prefixes(&self.announced_prefixes));
500        bytes.freeze()
501    }
502
503    /// Check if this is an end-of-rib message.
504    ///
505    /// <https://datatracker.ietf.org/doc/html/rfc4724#section-2>
506    /// End-of-rib message is a special update message that contains no NLRI or withdrawal NLRI prefixes.
507    pub fn is_end_of_rib(&self) -> bool {
508        // there are two cases for end-of-rib message:
509        // 1. IPv4 unicast address family: no announced, no withdrawn, no attributes
510        // 2. Other cases: no announced, no withdrawal, only MP_UNREACH_NRLI with no prefixes
511
512        if !self.announced_prefixes.is_empty() || !self.withdrawn_prefixes.is_empty() {
513            // has announced or withdrawal IPv4 unicast prefixes:
514            // definitely not end-of-rib
515
516            return false;
517        }
518
519        if self.attributes.inner.is_empty() {
520            // no attributes, no prefixes:
521            // case 1 end-of-rib
522            return true;
523        }
524
525        // has some attributes, it can only be withdrawal with no prefixes
526
527        if self.attributes.inner.len() > 1 {
528            // has more than one attributes, not end-of-rib
529            return false;
530        }
531
532        // has only one attribute, check if it is withdrawal attribute
533        if let AttributeValue::MpUnreachNlri(nlri) = &self.attributes.inner.first().unwrap().value {
534            if nlri.prefixes.is_empty() {
535                // the only attribute is MP_UNREACH_NLRI with no prefixes:
536                // case 2 end-of-rib
537                return true;
538            }
539        }
540
541        // all other cases: not end-of-rib
542        false
543    }
544}
545
546impl BgpMessage {
547    /// BGP marker value: 16 bytes of 0xFF (RFC 4271)
548    const MARKER: [u8; 16] = [0xFF; 16];
549
550    pub fn encode(&self, asn_len: AsnLength) -> Bytes {
551        let mut bytes = BytesMut::new();
552        // RFC 4271: Marker is 16 bytes of 0xFF
553        bytes.put_slice(&Self::MARKER);
554
555        let (msg_type, msg_bytes) = match self {
556            BgpMessage::Open(msg) => (BgpMessageType::OPEN, msg.encode()),
557            BgpMessage::Update(msg) => (BgpMessageType::UPDATE, msg.encode(asn_len)),
558            BgpMessage::Notification(msg) => (BgpMessageType::NOTIFICATION, msg.encode()),
559            BgpMessage::KeepAlive => (BgpMessageType::KEEPALIVE, Bytes::new()),
560        };
561
562        // msg total bytes length = msg bytes + 16 bytes marker + 2 bytes length + 1 byte type
563        bytes.put_u16(msg_bytes.len() as u16 + 16 + 2 + 1);
564        bytes.put_u8(msg_type as u8);
565        bytes.put_slice(&msg_bytes);
566        bytes.freeze()
567    }
568}
569
570impl From<&BgpElem> for BgpUpdateMessage {
571    fn from(elem: &BgpElem) -> Self {
572        BgpUpdateMessage {
573            withdrawn_prefixes: vec![],
574            attributes: Attributes::from(elem),
575            announced_prefixes: vec![],
576        }
577    }
578}
579
580impl From<BgpUpdateMessage> for BgpMessage {
581    fn from(value: BgpUpdateMessage) -> Self {
582        BgpMessage::Update(value)
583    }
584}
585
586#[cfg(test)]
587mod tests {
588    use super::*;
589    use std::net::Ipv4Addr;
590    use std::str::FromStr;
591
592    #[test]
593    fn test_end_of_rib() {
594        // No prefixes and empty attributes: end-of-rib
595        let attrs = Attributes::default();
596        let msg = BgpUpdateMessage {
597            withdrawn_prefixes: vec![],
598            attributes: attrs,
599            announced_prefixes: vec![],
600        };
601        assert!(msg.is_end_of_rib());
602
603        // single MP_UNREACH_NLRI attribute with no prefixes: end-of-rib
604        let attrs = Attributes::from_iter(vec![AttributeValue::MpUnreachNlri(Nlri {
605            afi: Afi::Ipv4,
606            safi: Safi::Unicast,
607            next_hop: None,
608            prefixes: vec![],
609            link_state_nlris: None,
610            flowspec_nlris: None,
611        })]);
612        let msg = BgpUpdateMessage {
613            withdrawn_prefixes: vec![],
614            attributes: attrs,
615            announced_prefixes: vec![],
616        };
617        assert!(msg.is_end_of_rib());
618
619        // message with announced prefixes
620        let prefix = NetworkPrefix::from_str("192.168.1.0/24").unwrap();
621        let attrs = Attributes::default();
622        let msg = BgpUpdateMessage {
623            withdrawn_prefixes: vec![],
624            attributes: attrs,
625            announced_prefixes: vec![prefix],
626        };
627        assert!(!msg.is_end_of_rib());
628
629        // message with withdrawn prefixes
630        let prefix = NetworkPrefix::from_str("192.168.1.0/24").unwrap();
631        let attrs = Attributes::default();
632        let msg = BgpUpdateMessage {
633            withdrawn_prefixes: vec![prefix],
634            attributes: attrs,
635            announced_prefixes: vec![],
636        };
637        assert!(!msg.is_end_of_rib());
638
639        // NLRI attribute with empty prefixes: NOT end-of-rib
640        let attrs = Attributes::from_iter(vec![AttributeValue::MpReachNlri(Nlri {
641            afi: Afi::Ipv4,
642            safi: Safi::Unicast,
643            next_hop: None,
644            prefixes: vec![],
645            link_state_nlris: None,
646            flowspec_nlris: None,
647        })]);
648        let msg = BgpUpdateMessage {
649            withdrawn_prefixes: vec![],
650            attributes: attrs,
651            announced_prefixes: vec![],
652        };
653        assert!(!msg.is_end_of_rib());
654
655        // NLRI attribute with non-empty prefixes
656        let attrs = Attributes::from_iter(vec![AttributeValue::MpReachNlri(Nlri {
657            afi: Afi::Ipv4,
658            safi: Safi::Unicast,
659            next_hop: None,
660            prefixes: vec![prefix],
661            link_state_nlris: None,
662            flowspec_nlris: None,
663        })]);
664        let msg = BgpUpdateMessage {
665            withdrawn_prefixes: vec![],
666            attributes: attrs,
667            announced_prefixes: vec![],
668        };
669        assert!(!msg.is_end_of_rib());
670
671        // Unreachable NLRI attribute with non-empty prefixes
672        let attrs = Attributes::from_iter(vec![AttributeValue::MpUnreachNlri(Nlri {
673            afi: Afi::Ipv4,
674            safi: Safi::Unicast,
675            next_hop: None,
676            prefixes: vec![prefix],
677            link_state_nlris: None,
678            flowspec_nlris: None,
679        })]);
680        let msg = BgpUpdateMessage {
681            withdrawn_prefixes: vec![],
682            attributes: attrs,
683            announced_prefixes: vec![],
684        };
685        assert!(!msg.is_end_of_rib());
686
687        // message with more than one attributes
688        let attrs = Attributes::from_iter(vec![
689            AttributeValue::MpUnreachNlri(Nlri {
690                afi: Afi::Ipv4,
691                safi: Safi::Unicast,
692                next_hop: None,
693                prefixes: vec![],
694                link_state_nlris: None,
695                flowspec_nlris: None,
696            }),
697            AttributeValue::AtomicAggregate,
698        ]);
699        let msg = BgpUpdateMessage {
700            withdrawn_prefixes: vec![],
701            attributes: attrs,
702            announced_prefixes: vec![],
703        };
704        assert!(!msg.is_end_of_rib());
705    }
706
707    #[test]
708    fn test_invlaid_length() {
709        let bytes = Bytes::from_static(&[
710            0x00, 0x00, 0x00, 0x00, // marker
711            0x00, 0x00, 0x00, 0x00, // marker
712            0x00, 0x00, 0x00, 0x00, // marker
713            0x00, 0x00, 0x00, 0x00, // marker
714            0x00, 0x00, // length
715            0x05, // type
716        ]);
717        let mut data = bytes.clone();
718        assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits16).is_err());
719
720        let bytes = Bytes::from_static(&[
721            0x00, 0x00, 0x00, 0x00, // marker
722            0x00, 0x00, 0x00, 0x00, // marker
723            0x00, 0x00, 0x00, 0x00, // marker
724            0x00, 0x00, 0x00, 0x00, // marker
725            0x00, 0x28, // length
726            0x05, // type
727        ]);
728        let mut data = bytes.clone();
729        assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits16).is_err());
730    }
731
732    #[test]
733    fn test_invlaid_type() {
734        let bytes = Bytes::from_static(&[
735            0xFF, 0xFF, 0xFF, 0xFF, // marker (valid RFC 4271)
736            0xFF, 0xFF, 0xFF, 0xFF, // marker
737            0xFF, 0xFF, 0xFF, 0xFF, // marker
738            0xFF, 0xFF, 0xFF, 0xFF, // marker
739            0x00, 0x28, // length
740            0x05, // type
741        ]);
742        let mut data = bytes.clone();
743        assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits16).is_err());
744    }
745
746    #[test]
747    fn test_bgp_message_length_underflow_protection() {
748        // Test that length values less than 19 are properly rejected
749        // without causing arithmetic underflow
750        for len in [0u16, 1, 18] {
751            let bytes = Bytes::from(vec![
752                0xFF,
753                0xFF,
754                0xFF,
755                0xFF, // marker
756                0xFF,
757                0xFF,
758                0xFF,
759                0xFF, // marker
760                0xFF,
761                0xFF,
762                0xFF,
763                0xFF, // marker
764                0xFF,
765                0xFF,
766                0xFF,
767                0xFF, // marker
768                (len >> 8) as u8,
769                (len & 0xFF) as u8, // length field
770                0x01,               // type = OPEN
771            ]);
772            let mut data = bytes.clone();
773            let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
774            assert!(
775                result.is_err(),
776                "Length {} should be rejected as invalid",
777                len
778            );
779        }
780    }
781
782    #[test]
783    fn test_bgp_marker_encoding_rfc4271() {
784        // Test that BgpMessage::encode produces correct RFC 4271 marker (all 0xFF)
785        let msg = BgpMessage::KeepAlive;
786        let encoded = msg.encode(AsnLength::Bits16);
787
788        // First 16 bytes should be all 0xFF
789        assert_eq!(
790            &encoded[..16],
791            &[0xFF; 16],
792            "BGP marker should be all 0xFF bytes"
793        );
794    }
795
796    #[test]
797    fn test_bgp_marker_validation() {
798        // Test that message with valid marker (all 0xFF) parses correctly
799        let valid_bytes = Bytes::from(vec![
800            0xFF, 0xFF, 0xFF, 0xFF, // marker
801            0xFF, 0xFF, 0xFF, 0xFF, // marker
802            0xFF, 0xFF, 0xFF, 0xFF, // marker
803            0xFF, 0xFF, 0xFF, 0xFF, // marker
804            0x00, 0x13, // length = 19 (minimum)
805            0x04, // type = KEEPALIVE
806        ]);
807        let mut data = valid_bytes.clone();
808        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
809        assert!(result.is_ok(), "Valid marker should parse successfully");
810
811        // Test that message with invalid marker (all zeros) is handled
812        // Parser should warn but still process (for MRT compatibility)
813        let invalid_bytes = Bytes::from(vec![
814            0x00, 0x00, 0x00, 0x00, // marker (invalid - should be 0xFF)
815            0x00, 0x00, 0x00, 0x00, // marker
816            0x00, 0x00, 0x00, 0x00, // marker
817            0x00, 0x00, 0x00, 0x00, // marker
818            0x00, 0x13, // length = 19
819            0x04, // type = KEEPALIVE
820        ]);
821        let mut data = invalid_bytes.clone();
822        // Should still parse (with warning) for compatibility
823        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
824        assert!(
825            result.is_ok(),
826            "Invalid marker should still parse (with warning)"
827        );
828    }
829
830    #[test]
831    fn test_attribute_length_overflow_protection() {
832        // Test that large attribute length values are handled correctly
833        // without causing overflow issues
834
835        // Create a BGP UPDATE message with attribute_length that exceeds available data
836        let update_bytes = Bytes::from(vec![
837            0x00, 0x00, // withdrawn length = 0
838            0xFF,
839            0xFF, // attribute length = 65535 (largest u16, but not enough data)
840                  // No actual attribute data follows
841        ]);
842
843        let result = parse_bgp_update_message(update_bytes, false, &AsnLength::Bits16);
844        assert!(
845            result.is_err(),
846            "Should fail when attribute_length exceeds available data"
847        );
848        assert!(
849            matches!(result, Err(ParserError::TruncatedMsg(_))),
850            "Should fail with TruncatedMsg error"
851        );
852
853        // Test valid attribute length parsing
854        let valid_update = Bytes::from(vec![
855            0x00, 0x00, // withdrawn length = 0
856            0x00,
857            0x00, // attribute length = 0
858                  // No attributes, valid empty UPDATE
859        ]);
860        let result = parse_bgp_update_message(valid_update, false, &AsnLength::Bits16);
861        assert!(result.is_ok(), "Should parse valid empty UPDATE");
862    }
863
864    #[test]
865    fn test_parse_bgp_notification_message() {
866        let bytes = Bytes::from_static(&[
867            0x01, // error code
868            0x02, // error subcode
869            0x00, 0x00, // data
870        ]);
871        let msg = parse_bgp_notification_message(bytes).unwrap();
872        matches!(
873            msg.error,
874            BgpError::MessageHeaderError(MessageHeaderError::BAD_MESSAGE_LENGTH)
875        );
876        assert_eq!(msg.data, Bytes::from_static(&[0x00, 0x00]));
877    }
878
879    #[test]
880    fn test_encode_bgp_notification_messsage() {
881        let msg = BgpNotificationMessage {
882            error: BgpError::MessageHeaderError(MessageHeaderError::BAD_MESSAGE_LENGTH),
883            data: vec![0x00, 0x00],
884        };
885        let bytes = msg.encode();
886        assert_eq!(bytes, Bytes::from_static(&[0x01, 0x02, 0x00, 0x00]));
887    }
888
889    #[test]
890    fn test_parse_bgp_open_message() {
891        let bytes = Bytes::from_static(&[
892            0x04, // version
893            0x00, 0x01, // asn
894            0x00, 0xb4, // hold time
895            0xc0, 0x00, 0x02, 0x01, // sender ip
896            0x00, // opt params length
897        ]);
898        let msg = parse_bgp_open_message(&mut bytes.clone()).unwrap();
899        assert_eq!(msg.version, 4);
900        assert_eq!(msg.asn, Asn::new_16bit(1));
901        assert_eq!(msg.hold_time, 180);
902        assert_eq!(msg.bgp_identifier, Ipv4Addr::new(192, 0, 2, 1));
903        assert!(!msg.extended_length);
904        assert_eq!(msg.opt_params.len(), 0);
905    }
906
907    #[test]
908    fn test_encode_bgp_open_message() {
909        let msg = BgpOpenMessage {
910            version: 4,
911            asn: Asn::new_16bit(1),
912            hold_time: 180,
913            bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
914            extended_length: false,
915            opt_params: vec![],
916        };
917        let bytes = msg.encode();
918        assert_eq!(
919            bytes,
920            Bytes::from_static(&[
921                0x04, // version
922                0x00, 0x01, // asn
923                0x00, 0xb4, // hold time
924                0xc0, 0x00, 0x02, 0x01, // sender ip
925                0x00, // opt params length
926            ])
927        );
928    }
929
930    #[test]
931    fn test_encode_bgp_notification_message() {
932        let bgp_message = BgpMessage::Notification(BgpNotificationMessage {
933            error: BgpError::MessageHeaderError(MessageHeaderError::BAD_MESSAGE_LENGTH),
934            data: vec![0x00, 0x00],
935        });
936        let bytes = bgp_message.encode(AsnLength::Bits16);
937        // RFC 4271: Marker is 16 bytes of 0xFF
938        assert_eq!(
939            bytes,
940            Bytes::from_static(&[
941                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // marker (8 bytes)
942                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // marker (8 bytes)
943                0x00, 0x17, // length = 23 (16 marker + 2 len + 1 type + 4 msg)
944                0x03, // type = NOTIFICATION
945                0x01, 0x02, // error code, subcode
946                0x00, 0x00 // data
947            ])
948        );
949    }
950
951    #[test]
952    fn test_bgp_message_from_bgp_update_message() {
953        let msg = BgpMessage::from(BgpUpdateMessage::default());
954        assert!(matches!(msg, BgpMessage::Update(_)));
955    }
956
957    #[test]
958    fn test_parse_bgp_open_message_with_extended_next_hop_capability() {
959        use crate::models::{Afi, Safi};
960
961        // BGP OPEN message with Extended Next Hop capability - RFC 8950, Section 3
962        // Version=4, ASN=65001, HoldTime=180, BGP-ID=192.0.2.1
963        // One capability: Extended Next Hop (type=5) with two entries:
964        // 1) IPv4 Unicast (AFI=1, SAFI=1) can use IPv6 NextHop (AFI=2)
965        // 2) IPv4 MPLS VPN (AFI=1, SAFI=128) can use IPv6 NextHop (AFI=2)
966        let bytes = Bytes::from(vec![
967            0x04, // version
968            0xfd, 0xe9, // asn = 65001
969            0x00, 0xb4, // hold time = 180
970            0xc0, 0x00, 0x02, 0x01, // sender ip = 192.0.2.1
971            0x10, // opt params length = 16
972            0x02, // param type = 2 (capability)
973            0x0e, // param length = 14
974            0x05, // capability type = 5 (Extended Next Hop)
975            0x0c, // capability length = 12 (2 entries * 6 bytes each)
976            0x00, 0x01, // NLRI AFI = 1 (IPv4)
977            0x00, 0x01, // NLRI SAFI = 1 (Unicast)
978            0x00, 0x02, // NextHop AFI = 2 (IPv6)
979            0x00, 0x01, // NLRI AFI = 1 (IPv4) - second entry
980            0x00, 0x80, // NLRI SAFI = 128 (MPLS VPN)
981            0x00, 0x02, // NextHop AFI = 2 (IPv6)
982        ]);
983
984        let msg = parse_bgp_open_message(&mut bytes.clone()).unwrap();
985        assert_eq!(msg.version, 4);
986        assert_eq!(msg.asn, Asn::new_16bit(65001));
987        assert_eq!(msg.hold_time, 180);
988        assert_eq!(msg.bgp_identifier, Ipv4Addr::new(192, 0, 2, 1));
989        assert!(!msg.extended_length);
990        assert_eq!(msg.opt_params.len(), 1);
991
992        // Check the capability
993        if let ParamValue::Capacities(cap) = &msg.opt_params[0].param_value {
994            assert_eq!(cap[0].ty, BgpCapabilityType::EXTENDED_NEXT_HOP_ENCODING);
995
996            if let CapabilityValue::ExtendedNextHop(enh_cap) = &cap[0].value {
997                assert_eq!(enh_cap.entries.len(), 2);
998
999                // Check first entry: IPv4 Unicast can use IPv6 NextHop
1000                let entry1 = &enh_cap.entries[0];
1001                assert_eq!(entry1.nlri_afi, Afi::Ipv4);
1002                assert_eq!(entry1.nlri_safi, Safi::Unicast);
1003                assert_eq!(entry1.nexthop_afi, Afi::Ipv6);
1004
1005                // Check second entry: IPv4 MPLS VPN can use IPv6 NextHop
1006                let entry2 = &enh_cap.entries[1];
1007                assert_eq!(entry2.nlri_afi, Afi::Ipv4);
1008                assert_eq!(entry2.nlri_safi, Safi::MplsVpn);
1009                assert_eq!(entry2.nexthop_afi, Afi::Ipv6);
1010
1011                // Test functionality
1012                assert!(enh_cap.supports(Afi::Ipv4, Safi::Unicast, Afi::Ipv6));
1013                assert!(enh_cap.supports(Afi::Ipv4, Safi::MplsVpn, Afi::Ipv6));
1014                assert!(!enh_cap.supports(Afi::Ipv4, Safi::Multicast, Afi::Ipv6));
1015            } else {
1016                panic!("Expected ExtendedNextHop capability value");
1017            }
1018        } else {
1019            panic!("Expected capability parameter");
1020        }
1021    }
1022
1023    #[test]
1024    fn test_rfc8654_extended_message_length_validation() {
1025        // Test valid extended UPDATE message (within 65535 limit)
1026        let bytes = Bytes::from_static(&[
1027            0x00, 0x00, 0x00, 0x00, // marker
1028            0x00, 0x00, 0x00, 0x00, // marker
1029            0x00, 0x00, 0x00, 0x00, // marker
1030            0x00, 0x00, 0x00, 0x00, // marker
1031            0x13, 0x00, // length = 4864 (0x1300) (extended message)
1032            0x02, // type = UPDATE
1033            0x00, 0x00, // withdrawn length = 0
1034            0x00,
1035            0x00, // path attribute length = 0
1036                  // No NLRI data needed for this test
1037        ]);
1038        let mut data = bytes.clone();
1039        // This should succeed because UPDATE messages can be extended
1040        assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits16).is_ok());
1041
1042        // Test OPEN message exceeding 4096 bytes (should fail)
1043        let bytes = Bytes::from_static(&[
1044            0x00, 0x00, 0x00, 0x00, // marker
1045            0x00, 0x00, 0x00, 0x00, // marker
1046            0x00, 0x00, 0x00, 0x00, // marker
1047            0x00, 0x00, 0x00, 0x00, // marker
1048            0x13, 0x00, // length = 4864 (0x1300) (exceeds 4096 for OPEN)
1049            0x01, // type = OPEN
1050        ]);
1051        let mut data = bytes.clone();
1052        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1053        assert!(result.is_err());
1054        if let Err(ParserError::ParseError(msg)) = result {
1055            assert!(msg.contains("BGP OPEN message length"));
1056            assert!(msg.contains("4096 bytes"));
1057        }
1058
1059        // Test KEEPALIVE message exceeding 4096 bytes (should fail)
1060        let bytes = Bytes::from_static(&[
1061            0x00, 0x00, 0x00, 0x00, // marker
1062            0x00, 0x00, 0x00, 0x00, // marker
1063            0x00, 0x00, 0x00, 0x00, // marker
1064            0x00, 0x00, 0x00, 0x00, // marker
1065            0x13, 0x00, // length = 4864 (0x1300) (exceeds 4096 for KEEPALIVE)
1066            0x04, // type = KEEPALIVE
1067        ]);
1068        let mut data = bytes.clone();
1069        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1070        assert!(result.is_err());
1071        if let Err(ParserError::ParseError(msg)) = result {
1072            assert!(msg.contains("BGP KEEPALIVE message length"));
1073            assert!(msg.contains("4096 bytes"));
1074        }
1075
1076        // Test message exceeding 65535 bytes (maximum allowed)
1077        let bytes = Bytes::from_static(&[
1078            0x00, 0x00, 0x00, 0x00, // marker
1079            0x00, 0x00, 0x00, 0x00, // marker
1080            0x00, 0x00, 0x00, 0x00, // marker
1081            0x00, 0x00, 0x00, 0x00, // marker
1082            0xFF, 0xFF, // length = 65535 (0xFFFF) (maximum allowed)
1083            0x02, // type = UPDATE
1084        ]);
1085        let mut data = bytes.clone();
1086        // This might fail due to insufficient data, but should not fail on length validation
1087        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1088        if let Err(ParserError::ParseError(msg)) = result {
1089            // Should not be a length validation error
1090            assert!(!msg.contains("invalid BGP message length"));
1091        }
1092    }
1093
1094    #[test]
1095    fn test_bgp_extended_message_capability_parsing() {
1096        use crate::models::CapabilityValue;
1097
1098        // Test BGP OPEN message with Extended Message capability (capability code 6)
1099        let bytes = Bytes::from(vec![
1100            0x04, // version
1101            0x00, 0x01, // asn
1102            0x00, 0xb4, // hold time
1103            0xc0, 0x00, 0x02, 0x01, // sender ip
1104            0x04, // opt params length = 4
1105            0x02, // param type = 2 (capability)
1106            0x02, // param length = 2
1107            0x06, // capability type = 6 (Extended Message)
1108            0x00, // capability length = 0 (no parameters)
1109        ]);
1110
1111        let msg = parse_bgp_open_message(&mut bytes.clone()).unwrap();
1112        assert_eq!(msg.version, 4);
1113        assert_eq!(msg.asn, Asn::new_16bit(1));
1114        assert_eq!(msg.opt_params.len(), 1);
1115
1116        // Check that we have the extended message capability
1117        if let ParamValue::Capacities(cap) = &msg.opt_params[0].param_value {
1118            assert_eq!(cap[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
1119            if let CapabilityValue::BgpExtendedMessage(_) = &cap[0].value {
1120                // Extended Message capability should have no parameters
1121            } else {
1122                panic!("Expected BgpExtendedMessage capability value");
1123            }
1124        } else {
1125            panic!("Expected capability parameter");
1126        }
1127    }
1128
1129    #[test]
1130    fn test_rfc8654_edge_cases() {
1131        // Test NOTIFICATION message with extended length (should be allowed)
1132        let bytes = Bytes::from_static(&[
1133            0x00, 0x00, 0x00, 0x00, // marker
1134            0x00, 0x00, 0x00, 0x00, // marker
1135            0x00, 0x00, 0x00, 0x00, // marker
1136            0x00, 0x00, 0x00, 0x00, // marker
1137            0x20, 0x00, // length = 8192 (extended NOTIFICATION message)
1138            0x03, // type = NOTIFICATION
1139            0x06, // error code (Cease)
1140            0x00, // error subcode
1141                  // Additional data would go here
1142        ]);
1143        let mut data = bytes.clone();
1144        // This should succeed because NOTIFICATION messages can be extended
1145        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1146        // May fail due to insufficient data, but not due to length validation
1147        if let Err(ParserError::ParseError(msg)) = result {
1148            assert!(!msg.contains("invalid BGP message length"));
1149            assert!(!msg.contains("exceeds maximum allowed 4096 bytes"));
1150        }
1151
1152        // Test message exactly at 4096 bytes for OPEN (should be allowed)
1153        let open_data = vec![
1154            0x00, 0x00, 0x00, 0x00, // marker
1155            0x00, 0x00, 0x00, 0x00, // marker
1156            0x00, 0x00, 0x00, 0x00, // marker
1157            0x00, 0x00, 0x00, 0x00, // marker
1158            0x10, 0x00, // length = 4096 (exactly at limit for OPEN)
1159            0x01, // type = OPEN
1160        ];
1161        let bytes = Bytes::from(open_data);
1162        let mut data = bytes.clone();
1163        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1164        // Should not fail on length validation (may fail on parsing due to insufficient data)
1165        if let Err(ParserError::ParseError(msg)) = result {
1166            assert!(!msg.contains("exceeds maximum allowed 4096 bytes"));
1167        }
1168
1169        // Test message exactly at 65535 bytes for UPDATE (should be allowed)
1170        let bytes = Bytes::from_static(&[
1171            0x00, 0x00, 0x00, 0x00, // marker
1172            0x00, 0x00, 0x00, 0x00, // marker
1173            0x00, 0x00, 0x00, 0x00, // marker
1174            0x00, 0x00, 0x00, 0x00, // marker
1175            0xFF, 0xFF, // length = 65535 (0xFFFF) (maximum allowed)
1176            0x02, // type = UPDATE
1177        ]);
1178        let mut data = bytes.clone();
1179        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1180        // Should not fail on length validation
1181        if let Err(ParserError::ParseError(msg)) = result {
1182            assert!(!msg.contains("invalid BGP message length"));
1183        }
1184    }
1185
1186    #[test]
1187    fn test_rfc8654_capability_encoding_path() {
1188        use crate::models::capabilities::BgpExtendedMessageCapability;
1189
1190        // Test that the encoding path for BgpExtendedMessage capability is covered
1191        // This specifically tests the line: CapabilityValue::BgpExtendedMessage(bem) => bem.encode()
1192        let capability_value =
1193            CapabilityValue::BgpExtendedMessage(BgpExtendedMessageCapability::new());
1194        let capability = Capability {
1195            ty: BgpCapabilityType::BGP_EXTENDED_MESSAGE,
1196            value: capability_value,
1197        };
1198
1199        let opt_param = OptParam {
1200            param_type: 2, // capability
1201            param_len: 2,
1202            param_value: ParamValue::Capacities(vec![capability]),
1203        };
1204
1205        let msg = BgpOpenMessage {
1206            version: 4,
1207            asn: Asn::new_16bit(65001),
1208            hold_time: 180,
1209            bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
1210            extended_length: false,
1211            opt_params: vec![opt_param],
1212        };
1213
1214        // This will exercise the encoding path we need to test
1215        let encoded = msg.encode();
1216        assert!(!encoded.is_empty());
1217
1218        // Verify we can parse it back (exercises the parsing path too)
1219        let parsed = parse_bgp_open_message(&mut encoded.clone()).unwrap();
1220        assert_eq!(parsed.opt_params.len(), 1);
1221        if let ParamValue::Capacities(cap) = &parsed.opt_params[0].param_value {
1222            assert_eq!(cap[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
1223        }
1224    }
1225
1226    #[test]
1227    fn test_rfc8654_error_message_formatting() {
1228        // Test the error message formatting paths that include message type names
1229        // This tests the match arms for OPEN and KEEPALIVE in error messages
1230
1231        // Test OPEN message error path
1232        let bytes = Bytes::from_static(&[
1233            0x00, 0x00, 0x00, 0x00, // marker
1234            0x00, 0x00, 0x00, 0x00, // marker
1235            0x00, 0x00, 0x00, 0x00, // marker
1236            0x00, 0x00, 0x00, 0x00, // marker
1237            0x20, 0x01, // length = 8193 (exceeds 4096 for OPEN)
1238            0x01, // type = OPEN
1239        ]);
1240        let mut data = bytes.clone();
1241        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1242        assert!(result.is_err());
1243        if let Err(ParserError::ParseError(msg)) = result {
1244            assert!(msg.contains("BGP OPEN message length"));
1245            assert!(msg.contains("exceeds maximum allowed 4096 bytes"));
1246        }
1247
1248        // Test KEEPALIVE message error path
1249        let bytes = Bytes::from_static(&[
1250            0x00, 0x00, 0x00, 0x00, // marker
1251            0x00, 0x00, 0x00, 0x00, // marker
1252            0x00, 0x00, 0x00, 0x00, // marker
1253            0x00, 0x00, 0x00, 0x00, // marker
1254            0x20, 0x01, // length = 8193 (exceeds 4096 for KEEPALIVE)
1255            0x04, // type = KEEPALIVE
1256        ]);
1257        let mut data = bytes.clone();
1258        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1259        assert!(result.is_err());
1260        if let Err(ParserError::ParseError(msg)) = result {
1261            assert!(msg.contains("BGP KEEPALIVE message length"));
1262            assert!(msg.contains("exceeds maximum allowed 4096 bytes"));
1263        }
1264    }
1265
1266    #[test]
1267    fn test_encode_bgp_open_message_with_extended_message_capability() {
1268        use crate::models::capabilities::BgpExtendedMessageCapability;
1269
1270        // Create Extended Message capability
1271        let extended_msg_capability = BgpExtendedMessageCapability::new();
1272
1273        let msg = BgpOpenMessage {
1274            version: 4,
1275            asn: Asn::new_16bit(65001),
1276            hold_time: 180,
1277            bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
1278            extended_length: false,
1279            opt_params: vec![OptParam {
1280                param_type: 2, // capability
1281                param_len: 2,  // 1 (type) + 1 (len) + 0 (no parameters)
1282                param_value: ParamValue::Capacities(vec![Capability {
1283                    ty: BgpCapabilityType::BGP_EXTENDED_MESSAGE,
1284                    value: CapabilityValue::BgpExtendedMessage(extended_msg_capability),
1285                }]),
1286            }],
1287        };
1288
1289        let encoded = msg.encode();
1290
1291        // Parse the encoded message back and verify it matches
1292        let parsed = parse_bgp_open_message(&mut encoded.clone()).unwrap();
1293        assert_eq!(parsed.version, msg.version);
1294        assert_eq!(parsed.asn, msg.asn);
1295        assert_eq!(parsed.hold_time, msg.hold_time);
1296        assert_eq!(parsed.bgp_identifier, msg.bgp_identifier);
1297        assert_eq!(parsed.opt_params.len(), 1);
1298
1299        // Verify the capability was encoded and parsed correctly
1300        if let ParamValue::Capacities(cap) = &parsed.opt_params[0].param_value {
1301            assert_eq!(cap[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
1302            if let CapabilityValue::BgpExtendedMessage(_) = &cap[0].value {
1303                // Extended Message capability should have no parameters
1304            } else {
1305                panic!("Expected BgpExtendedMessage capability value after round trip");
1306            }
1307        } else {
1308            panic!("Expected capability parameter after round trip");
1309        }
1310    }
1311
1312    #[test]
1313    fn test_encode_bgp_open_message_with_extended_next_hop_capability() {
1314        use crate::models::capabilities::{ExtendedNextHopCapability, ExtendedNextHopEntry};
1315        use crate::models::{Afi, Safi};
1316
1317        // Create Extended Next Hop capability
1318        let entries = vec![
1319            ExtendedNextHopEntry {
1320                nlri_afi: Afi::Ipv4,
1321                nlri_safi: Safi::Unicast,
1322                nexthop_afi: Afi::Ipv6,
1323            },
1324            ExtendedNextHopEntry {
1325                nlri_afi: Afi::Ipv4,
1326                nlri_safi: Safi::MplsVpn,
1327                nexthop_afi: Afi::Ipv6,
1328            },
1329        ];
1330        let enh_capability = ExtendedNextHopCapability::new(entries);
1331
1332        let msg = BgpOpenMessage {
1333            version: 4,
1334            asn: Asn::new_16bit(65001),
1335            hold_time: 180,
1336            bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
1337            extended_length: false,
1338            opt_params: vec![OptParam {
1339                param_type: 2, // capability
1340                param_len: 14, // 1 (type) + 1 (len) + 12 (2 entries * 6 bytes)
1341                param_value: ParamValue::Capacities(vec![Capability {
1342                    ty: BgpCapabilityType::EXTENDED_NEXT_HOP_ENCODING,
1343                    value: CapabilityValue::ExtendedNextHop(enh_capability),
1344                }]),
1345            }],
1346        };
1347
1348        let encoded = msg.encode();
1349
1350        // Parse the encoded message back and verify it matches
1351        let parsed = parse_bgp_open_message(&mut encoded.clone()).unwrap();
1352        assert_eq!(parsed.version, msg.version);
1353        assert_eq!(parsed.asn, msg.asn);
1354        assert_eq!(parsed.hold_time, msg.hold_time);
1355        assert_eq!(parsed.bgp_identifier, msg.bgp_identifier);
1356        assert_eq!(parsed.extended_length, msg.extended_length);
1357        assert_eq!(parsed.opt_params.len(), 1);
1358
1359        // Verify the capability was encoded and parsed correctly
1360        if let ParamValue::Capacities(cap) = &parsed.opt_params[0].param_value {
1361            assert_eq!(cap[0].ty, BgpCapabilityType::EXTENDED_NEXT_HOP_ENCODING);
1362            if let CapabilityValue::ExtendedNextHop(enh_cap) = &cap[0].value {
1363                assert_eq!(enh_cap.entries.len(), 2);
1364                assert!(enh_cap.supports(Afi::Ipv4, Safi::Unicast, Afi::Ipv6));
1365                assert!(enh_cap.supports(Afi::Ipv4, Safi::MplsVpn, Afi::Ipv6));
1366            } else {
1367                panic!("Expected ExtendedNextHop capability value after round trip");
1368            }
1369        } else {
1370            panic!("Expected capability parameter after round trip");
1371        }
1372    }
1373
1374    #[test]
1375    fn test_parse_bgp_open_message_with_multiple_capabilities() {
1376        // Create a BGP OPEN message with multiple capabilities in a single optional parameter
1377        // This tests RFC 5492 support for multiple capabilities per parameter
1378
1379        // Build capabilities: Extended Message, Route Refresh, and 4-octet AS
1380        let extended_msg_cap = Capability {
1381            ty: BgpCapabilityType::BGP_EXTENDED_MESSAGE,
1382            value: CapabilityValue::BgpExtendedMessage(BgpExtendedMessageCapability {}),
1383        };
1384
1385        let route_refresh_cap = Capability {
1386            ty: BgpCapabilityType::ROUTE_REFRESH_CAPABILITY_FOR_BGP_4,
1387            value: CapabilityValue::RouteRefresh(RouteRefreshCapability {}),
1388        };
1389
1390        let four_octet_as_cap = Capability {
1391            ty: BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY,
1392            value: CapabilityValue::FourOctetAs(FourOctetAsCapability { asn: 65536 }),
1393        };
1394
1395        // Create OPEN message with all three capabilities in one parameter
1396        let msg = BgpOpenMessage {
1397            version: 4,
1398            asn: Asn::new_32bit(65000),
1399            hold_time: 180,
1400            bgp_identifier: "10.0.0.1".parse().unwrap(),
1401            extended_length: false,
1402            opt_params: vec![OptParam {
1403                param_type: 2, // capability
1404                param_len: 10, // 3 capabilities: (1+1+0) + (1+1+0) + (1+1+4) = 2+2+6 = 10
1405                param_value: ParamValue::Capacities(vec![
1406                    extended_msg_cap,
1407                    route_refresh_cap,
1408                    four_octet_as_cap,
1409                ]),
1410            }],
1411        };
1412
1413        // Encode the message
1414        let encoded = msg.encode();
1415
1416        // Parse it back
1417        let mut encoded_bytes = encoded.clone();
1418        let parsed = parse_bgp_open_message(&mut encoded_bytes).unwrap();
1419
1420        // Verify basic fields
1421        assert_eq!(parsed.version, 4);
1422        assert_eq!(parsed.asn, Asn::new_32bit(65000));
1423        assert_eq!(parsed.hold_time, 180);
1424        assert_eq!(
1425            parsed.bgp_identifier,
1426            "10.0.0.1".parse::<std::net::Ipv4Addr>().unwrap()
1427        );
1428        assert_eq!(parsed.opt_params.len(), 1);
1429
1430        // Verify we have all three capabilities
1431        if let ParamValue::Capacities(caps) = &parsed.opt_params[0].param_value {
1432            assert_eq!(caps.len(), 3, "Should have 3 capabilities");
1433
1434            // Check first capability: Extended Message
1435            assert_eq!(caps[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
1436            assert!(matches!(
1437                caps[0].value,
1438                CapabilityValue::BgpExtendedMessage(_)
1439            ));
1440
1441            // Check second capability: Route Refresh
1442            assert_eq!(
1443                caps[1].ty,
1444                BgpCapabilityType::ROUTE_REFRESH_CAPABILITY_FOR_BGP_4
1445            );
1446            assert!(matches!(caps[1].value, CapabilityValue::RouteRefresh(_)));
1447
1448            // Check third capability: 4-octet AS
1449            assert_eq!(
1450                caps[2].ty,
1451                BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY
1452            );
1453            if let CapabilityValue::FourOctetAs(foa) = &caps[2].value {
1454                assert_eq!(foa.asn, 65536);
1455            } else {
1456                panic!("Expected FourOctetAs capability value");
1457            }
1458        } else {
1459            panic!("Expected Capacities parameter");
1460        }
1461    }
1462
1463    #[test]
1464    fn test_parse_bgp_open_message_with_multiple_capability_parameters() {
1465        // Test parsing OPEN message with multiple optional parameters, each containing capabilities
1466        // This is less common but still valid per RFC 5492
1467
1468        let msg = BgpOpenMessage {
1469            version: 4,
1470            asn: Asn::new_32bit(65001),
1471            hold_time: 90,
1472            bgp_identifier: "192.168.1.1".parse().unwrap(),
1473            extended_length: false,
1474            opt_params: vec![
1475                OptParam {
1476                    param_type: 2, // capability
1477                    param_len: 2,
1478                    param_value: ParamValue::Capacities(vec![Capability {
1479                        ty: BgpCapabilityType::BGP_EXTENDED_MESSAGE,
1480                        value: CapabilityValue::BgpExtendedMessage(BgpExtendedMessageCapability {}),
1481                    }]),
1482                },
1483                OptParam {
1484                    param_type: 2, // capability
1485                    param_len: 6,
1486                    param_value: ParamValue::Capacities(vec![Capability {
1487                        ty: BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY,
1488                        value: CapabilityValue::FourOctetAs(FourOctetAsCapability {
1489                            asn: 4200000000,
1490                        }),
1491                    }]),
1492                },
1493            ],
1494        };
1495
1496        // Encode and parse back
1497        let encoded = msg.encode();
1498        let mut encoded_bytes = encoded.clone();
1499        let parsed = parse_bgp_open_message(&mut encoded_bytes).unwrap();
1500
1501        // Verify we have 2 optional parameters
1502        assert_eq!(parsed.opt_params.len(), 2);
1503
1504        // Check first parameter
1505        if let ParamValue::Capacities(caps) = &parsed.opt_params[0].param_value {
1506            assert_eq!(caps.len(), 1);
1507            assert_eq!(caps[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
1508        } else {
1509            panic!("Expected Capacities in first parameter");
1510        }
1511
1512        // Check second parameter
1513        if let ParamValue::Capacities(caps) = &parsed.opt_params[1].param_value {
1514            assert_eq!(caps.len(), 1);
1515            assert_eq!(
1516                caps[0].ty,
1517                BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY
1518            );
1519            if let CapabilityValue::FourOctetAs(foa) = &caps[0].value {
1520                assert_eq!(foa.asn, 4200000000);
1521            }
1522        } else {
1523            panic!("Expected Capacities in second parameter");
1524        }
1525    }
1526}