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