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::{BgpValidationWarning, 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.
429///
430/// Returns `Ok(vec![])` for empty NLRI. Returns `Err` for malformed NLRI
431/// (invalid prefix lengths, truncated data), so that the caller
432/// in `parse_bgp_update_message` can convert it to a `MalformedNlri` warning.
433fn read_nlri(input: Bytes, afi: &Afi, add_path: bool) -> Result<Vec<NetworkPrefix>, ParserError> {
434    let length = input.len();
435    if length == 0 {
436        return Ok(vec![]);
437    }
438    if length == 1 && input[0] != 0 {
439        // A single non-zero byte cannot be a valid NLRI: a valid 1-byte NLRI
440        // encodes only the default route (prefix length 0, no prefix octets).
441        warn!("seeing strange one-byte NLRI field (parsing NLRI in BGP UPDATE message)");
442        return Err(ParserError::ParseError(
443            "one-byte NLRI field with non-zero value is not a valid encoding".to_string(),
444        ));
445    }
446
447    parse_nlri_list(input, add_path, afi)
448}
449
450/// read bgp update message.
451///
452/// RFC: <https://tools.ietf.org/html/rfc4271#section-4.3>
453///
454/// Per RFC 7606, NLRI parse errors are non-fatal: the message is returned with
455/// partial data and a [`BgpValidationWarning::MalformedNlri`] appended to
456/// `attributes.validation_warnings`. Callers that want RFC 7606 treat-as-withdrawal
457/// semantics can inspect the warnings and act accordingly. Attribute-section
458/// framing errors (wrong length fields, truncated data) remain fatal.
459pub fn parse_bgp_update_message(
460    mut input: Bytes,
461    add_path: bool,
462    asn_len: &AsnLength,
463) -> Result<BgpUpdateMessage, ParserError> {
464    // NOTE: AFI for routes outside attributes are IPv4 ONLY.
465    let afi = Afi::Ipv4;
466
467    // parse withdrawn prefixes NLRI
468    let withdrawn_bytes_length_raw = input.read_u16()?;
469    let withdrawn_bytes_length = withdrawn_bytes_length_raw as usize;
470    input.has_n_remaining(withdrawn_bytes_length)?;
471    let withdrawn_bytes = input.split_to(withdrawn_bytes_length);
472    let (withdrawn_prefixes, withdrawn_nlri_error) =
473        match read_nlri(withdrawn_bytes.clone(), &afi, add_path) {
474            Ok(pfxs) => (pfxs, None),
475            Err(e) => (
476                Vec::new(),
477                Some(BgpValidationWarning::MalformedNlri {
478                    nlri_type: "withdrawn",
479                    reason: e.to_string(),
480                    raw_bytes: withdrawn_bytes.to_vec(),
481                }),
482            ),
483        };
484
485    // parse attributes
486    let attribute_length_raw = input.read_u16()?;
487    // Defensive check: ensure attribute_length fits in usize
488    // u16 to usize conversion is always safe on 32/64-bit platforms,
489    // but this check ensures safety on all architectures
490    let attribute_length = attribute_length_raw as usize;
491
492    input.has_n_remaining(attribute_length)?;
493    let attr_data_slice = input.split_to(attribute_length);
494    let mut attributes = parse_attributes(attr_data_slice, asn_len, add_path, None, None, None)?;
495
496    // parse announced prefixes nlri.
497    // the remaining bytes are announced prefixes.
498    let announced_bytes_present = !input.is_empty();
499    let (announced_prefixes, announced_nlri_error) = match read_nlri(input.clone(), &afi, add_path)
500    {
501        Ok(pfxs) => (pfxs, None),
502        Err(e) => (
503            Vec::new(),
504            Some(BgpValidationWarning::MalformedNlri {
505                nlri_type: "announced",
506                reason: e.to_string(),
507                raw_bytes: input.to_vec(),
508            }),
509        ),
510    };
511
512    // validate mandatory attributes.
513    // Use `announced_bytes_present` (wire-level) rather than
514    // `!announced_prefixes.is_empty()` (parse result) so that a malformed
515    // NLRI section still triggers mandatory-attribute validation — the
516    // UPDATE clearly intended to announce routes.
517    let is_announcement =
518        announced_bytes_present || attributes.has_attr(AttrType::MP_REACHABLE_NLRI);
519    let has_standard_nlri = announced_bytes_present;
520    attributes.check_mandatory_attributes(is_announcement, has_standard_nlri);
521
522    // Attach NLRI parse warnings (RFC 7606 §5.3 treat-as-withdrawal evidence)
523    if let Some(w) = withdrawn_nlri_error {
524        attributes.add_validation_warning(w);
525    }
526    if let Some(w) = announced_nlri_error {
527        attributes.add_validation_warning(w);
528    }
529
530    Ok(BgpUpdateMessage {
531        withdrawn_prefixes,
532        attributes,
533        announced_prefixes,
534    })
535}
536
537impl BgpUpdateMessage {
538    pub fn encode(&self, asn_len: AsnLength) -> Bytes {
539        let mut bytes = BytesMut::new();
540
541        // withdrawn prefixes
542        let withdrawn_bytes = encode_nlri_prefixes(&self.withdrawn_prefixes);
543        bytes.put_u16(withdrawn_bytes.len() as u16);
544        bytes.put_slice(&withdrawn_bytes);
545
546        // attributes
547        let attr_bytes = self.attributes.encode(asn_len);
548
549        bytes.put_u16(attr_bytes.len() as u16);
550        bytes.put_slice(&attr_bytes);
551
552        bytes.extend(encode_nlri_prefixes(&self.announced_prefixes));
553        bytes.freeze()
554    }
555
556    /// Check if this is an end-of-rib message.
557    ///
558    /// <https://datatracker.ietf.org/doc/html/rfc4724#section-2>
559    /// End-of-rib message is a special update message that contains no NLRI or withdrawal NLRI prefixes.
560    pub fn is_end_of_rib(&self) -> bool {
561        // there are two cases for end-of-rib message:
562        // 1. IPv4 unicast address family: no announced, no withdrawn, no attributes
563        // 2. Other cases: no announced, no withdrawal, only MP_UNREACH_NRLI with no prefixes
564
565        if !self.announced_prefixes.is_empty() || !self.withdrawn_prefixes.is_empty() {
566            // has announced or withdrawal IPv4 unicast prefixes:
567            // definitely not end-of-rib
568
569            return false;
570        }
571
572        if self.attributes.inner.is_empty() {
573            // no attributes, no prefixes:
574            // case 1 end-of-rib
575            return true;
576        }
577
578        // has some attributes, it can only be withdrawal with no prefixes
579
580        if self.attributes.inner.len() > 1 {
581            // has more than one attributes, not end-of-rib
582            return false;
583        }
584
585        // has only one attribute, check if it is withdrawal attribute
586        if let AttributeValue::MpUnreachNlri(nlri) = &self.attributes.inner.first().unwrap().value {
587            if nlri.prefixes.is_empty() {
588                // the only attribute is MP_UNREACH_NLRI with no prefixes:
589                // case 2 end-of-rib
590                return true;
591            }
592        }
593
594        // all other cases: not end-of-rib
595        false
596    }
597}
598
599impl BgpMessage {
600    /// BGP marker value: 16 bytes of 0xFF (RFC 4271)
601    const MARKER: [u8; 16] = [0xFF; 16];
602
603    pub fn encode(&self, asn_len: AsnLength) -> Bytes {
604        let mut bytes = BytesMut::new();
605        // RFC 4271: Marker is 16 bytes of 0xFF
606        bytes.put_slice(&Self::MARKER);
607
608        let (msg_type, msg_bytes) = match self {
609            BgpMessage::Open(msg) => (BgpMessageType::OPEN, msg.encode()),
610            BgpMessage::Update(msg) => (BgpMessageType::UPDATE, msg.encode(asn_len)),
611            BgpMessage::Notification(msg) => (BgpMessageType::NOTIFICATION, msg.encode()),
612            BgpMessage::KeepAlive => (BgpMessageType::KEEPALIVE, Bytes::new()),
613        };
614
615        // msg total bytes length = msg bytes + 16 bytes marker + 2 bytes length + 1 byte type
616        bytes.put_u16(msg_bytes.len() as u16 + 16 + 2 + 1);
617        bytes.put_u8(msg_type as u8);
618        bytes.put_slice(&msg_bytes);
619        bytes.freeze()
620    }
621}
622
623impl From<&BgpElem> for BgpUpdateMessage {
624    fn from(elem: &BgpElem) -> Self {
625        BgpUpdateMessage {
626            withdrawn_prefixes: vec![],
627            attributes: Attributes::from(elem),
628            announced_prefixes: vec![],
629        }
630    }
631}
632
633impl From<BgpUpdateMessage> for BgpMessage {
634    fn from(value: BgpUpdateMessage) -> Self {
635        BgpMessage::Update(value)
636    }
637}
638
639#[cfg(test)]
640mod tests {
641    use super::*;
642    use std::net::Ipv4Addr;
643    use std::str::FromStr;
644
645    #[test]
646    fn test_end_of_rib() {
647        // No prefixes and empty attributes: end-of-rib
648        let attrs = Attributes::default();
649        let msg = BgpUpdateMessage {
650            withdrawn_prefixes: vec![],
651            attributes: attrs,
652            announced_prefixes: vec![],
653        };
654        assert!(msg.is_end_of_rib());
655
656        // single MP_UNREACH_NLRI attribute with no prefixes: end-of-rib
657        let attrs = Attributes::from_iter(vec![AttributeValue::MpUnreachNlri(Nlri {
658            afi: Afi::Ipv4,
659            safi: Safi::Unicast,
660            next_hop: None,
661            prefixes: vec![],
662            labeled_prefixes: None,
663            link_state_nlris: None,
664            flowspec_nlris: None,
665        })]);
666        let msg = BgpUpdateMessage {
667            withdrawn_prefixes: vec![],
668            attributes: attrs,
669            announced_prefixes: vec![],
670        };
671        assert!(msg.is_end_of_rib());
672
673        // message with announced prefixes
674        let prefix = NetworkPrefix::from_str("192.168.1.0/24").unwrap();
675        let attrs = Attributes::default();
676        let msg = BgpUpdateMessage {
677            withdrawn_prefixes: vec![],
678            attributes: attrs,
679            announced_prefixes: vec![prefix],
680        };
681        assert!(!msg.is_end_of_rib());
682
683        // message with withdrawn prefixes
684        let prefix = NetworkPrefix::from_str("192.168.1.0/24").unwrap();
685        let attrs = Attributes::default();
686        let msg = BgpUpdateMessage {
687            withdrawn_prefixes: vec![prefix],
688            attributes: attrs,
689            announced_prefixes: vec![],
690        };
691        assert!(!msg.is_end_of_rib());
692
693        // NLRI attribute with empty prefixes: NOT end-of-rib
694        let attrs = Attributes::from_iter(vec![AttributeValue::MpReachNlri(Nlri {
695            afi: Afi::Ipv4,
696            safi: Safi::Unicast,
697            next_hop: None,
698            prefixes: vec![],
699            labeled_prefixes: None,
700            link_state_nlris: None,
701            flowspec_nlris: None,
702        })]);
703        let msg = BgpUpdateMessage {
704            withdrawn_prefixes: vec![],
705            attributes: attrs,
706            announced_prefixes: vec![],
707        };
708        assert!(!msg.is_end_of_rib());
709
710        // NLRI attribute with non-empty prefixes
711        let attrs = Attributes::from_iter(vec![AttributeValue::MpReachNlri(Nlri {
712            afi: Afi::Ipv4,
713            safi: Safi::Unicast,
714            next_hop: None,
715            prefixes: vec![prefix],
716            labeled_prefixes: None,
717            link_state_nlris: None,
718            flowspec_nlris: None,
719        })]);
720        let msg = BgpUpdateMessage {
721            withdrawn_prefixes: vec![],
722            attributes: attrs,
723            announced_prefixes: vec![],
724        };
725        assert!(!msg.is_end_of_rib());
726
727        // Unreachable NLRI attribute with non-empty prefixes
728        let attrs = Attributes::from_iter(vec![AttributeValue::MpUnreachNlri(Nlri {
729            afi: Afi::Ipv4,
730            safi: Safi::Unicast,
731            next_hop: None,
732            prefixes: vec![prefix],
733            labeled_prefixes: None,
734            link_state_nlris: None,
735            flowspec_nlris: None,
736        })]);
737        let msg = BgpUpdateMessage {
738            withdrawn_prefixes: vec![],
739            attributes: attrs,
740            announced_prefixes: vec![],
741        };
742        assert!(!msg.is_end_of_rib());
743
744        // message with more than one attributes
745        let attrs = Attributes::from_iter(vec![
746            AttributeValue::MpUnreachNlri(Nlri {
747                afi: Afi::Ipv4,
748                safi: Safi::Unicast,
749                next_hop: None,
750                prefixes: vec![],
751                labeled_prefixes: None,
752                link_state_nlris: None,
753                flowspec_nlris: None,
754            }),
755            AttributeValue::AtomicAggregate,
756        ]);
757        let msg = BgpUpdateMessage {
758            withdrawn_prefixes: vec![],
759            attributes: attrs,
760            announced_prefixes: vec![],
761        };
762        assert!(!msg.is_end_of_rib());
763    }
764
765    #[test]
766    fn test_invlaid_length() {
767        let bytes = Bytes::from_static(&[
768            0x00, 0x00, 0x00, 0x00, // marker
769            0x00, 0x00, 0x00, 0x00, // marker
770            0x00, 0x00, 0x00, 0x00, // marker
771            0x00, 0x00, 0x00, 0x00, // marker
772            0x00, 0x00, // length
773            0x05, // type
774        ]);
775        let mut data = bytes.clone();
776        assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits16).is_err());
777
778        let bytes = Bytes::from_static(&[
779            0x00, 0x00, 0x00, 0x00, // marker
780            0x00, 0x00, 0x00, 0x00, // marker
781            0x00, 0x00, 0x00, 0x00, // marker
782            0x00, 0x00, 0x00, 0x00, // marker
783            0x00, 0x28, // length
784            0x05, // type
785        ]);
786        let mut data = bytes.clone();
787        assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits16).is_err());
788    }
789
790    #[test]
791    fn test_invlaid_type() {
792        let bytes = Bytes::from_static(&[
793            0xFF, 0xFF, 0xFF, 0xFF, // marker (valid RFC 4271)
794            0xFF, 0xFF, 0xFF, 0xFF, // marker
795            0xFF, 0xFF, 0xFF, 0xFF, // marker
796            0xFF, 0xFF, 0xFF, 0xFF, // marker
797            0x00, 0x28, // length
798            0x05, // type
799        ]);
800        let mut data = bytes.clone();
801        assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits16).is_err());
802    }
803
804    #[test]
805    fn test_bgp_message_length_underflow_protection() {
806        // Test that length values less than 19 are properly rejected
807        // without causing arithmetic underflow
808        for len in [0u16, 1, 18] {
809            let bytes = Bytes::from(vec![
810                0xFF,
811                0xFF,
812                0xFF,
813                0xFF, // marker
814                0xFF,
815                0xFF,
816                0xFF,
817                0xFF, // marker
818                0xFF,
819                0xFF,
820                0xFF,
821                0xFF, // marker
822                0xFF,
823                0xFF,
824                0xFF,
825                0xFF, // marker
826                (len >> 8) as u8,
827                (len & 0xFF) as u8, // length field
828                0x01,               // type = OPEN
829            ]);
830            let mut data = bytes.clone();
831            let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
832            assert!(
833                result.is_err(),
834                "Length {} should be rejected as invalid",
835                len
836            );
837        }
838    }
839
840    #[test]
841    fn test_bgp_marker_encoding_rfc4271() {
842        // Test that BgpMessage::encode produces correct RFC 4271 marker (all 0xFF)
843        let msg = BgpMessage::KeepAlive;
844        let encoded = msg.encode(AsnLength::Bits16);
845
846        // First 16 bytes should be all 0xFF
847        assert_eq!(
848            &encoded[..16],
849            &[0xFF; 16],
850            "BGP marker should be all 0xFF bytes"
851        );
852    }
853
854    #[test]
855    fn test_bgp_marker_validation() {
856        // Test that message with valid marker (all 0xFF) parses correctly
857        let valid_bytes = Bytes::from(vec![
858            0xFF, 0xFF, 0xFF, 0xFF, // marker
859            0xFF, 0xFF, 0xFF, 0xFF, // marker
860            0xFF, 0xFF, 0xFF, 0xFF, // marker
861            0xFF, 0xFF, 0xFF, 0xFF, // marker
862            0x00, 0x13, // length = 19 (minimum)
863            0x04, // type = KEEPALIVE
864        ]);
865        let mut data = valid_bytes.clone();
866        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
867        assert!(result.is_ok(), "Valid marker should parse successfully");
868
869        // Test that message with invalid marker (all zeros) is handled
870        // Parser should warn but still process (for MRT compatibility)
871        let invalid_bytes = Bytes::from(vec![
872            0x00, 0x00, 0x00, 0x00, // marker (invalid - should be 0xFF)
873            0x00, 0x00, 0x00, 0x00, // marker
874            0x00, 0x00, 0x00, 0x00, // marker
875            0x00, 0x00, 0x00, 0x00, // marker
876            0x00, 0x13, // length = 19
877            0x04, // type = KEEPALIVE
878        ]);
879        let mut data = invalid_bytes.clone();
880        // Should still parse (with warning) for compatibility
881        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
882        assert!(
883            result.is_ok(),
884            "Invalid marker should still parse (with warning)"
885        );
886    }
887
888    #[test]
889    fn test_attribute_length_overflow_protection() {
890        // Test that large attribute length values are handled correctly
891        // without causing overflow issues
892
893        // Create a BGP UPDATE message with attribute_length that exceeds available data
894        let update_bytes = Bytes::from(vec![
895            0x00, 0x00, // withdrawn length = 0
896            0xFF,
897            0xFF, // attribute length = 65535 (largest u16, but not enough data)
898                  // No actual attribute data follows
899        ]);
900
901        let result = parse_bgp_update_message(update_bytes, false, &AsnLength::Bits16);
902        assert!(
903            result.is_err(),
904            "Should fail when attribute_length exceeds available data"
905        );
906        assert!(
907            matches!(result, Err(ParserError::TruncatedMsg(_))),
908            "Should fail with TruncatedMsg error"
909        );
910
911        // Test valid attribute length parsing
912        let valid_update = Bytes::from(vec![
913            0x00, 0x00, // withdrawn length = 0
914            0x00,
915            0x00, // attribute length = 0
916                  // No attributes, valid empty UPDATE
917        ]);
918        let result = parse_bgp_update_message(valid_update, false, &AsnLength::Bits16);
919        assert!(result.is_ok(), "Should parse valid empty UPDATE");
920    }
921
922    #[test]
923    fn test_parse_bgp_notification_message() {
924        let bytes = Bytes::from_static(&[
925            0x01, // error code
926            0x02, // error subcode
927            0x00, 0x00, // data
928        ]);
929        let msg = parse_bgp_notification_message(bytes).unwrap();
930        matches!(
931            msg.error,
932            BgpError::MessageHeaderError(MessageHeaderError::BAD_MESSAGE_LENGTH)
933        );
934        assert_eq!(msg.data, Bytes::from_static(&[0x00, 0x00]));
935    }
936
937    #[test]
938    fn test_encode_bgp_notification_messsage() {
939        let msg = BgpNotificationMessage {
940            error: BgpError::MessageHeaderError(MessageHeaderError::BAD_MESSAGE_LENGTH),
941            data: vec![0x00, 0x00],
942        };
943        let bytes = msg.encode();
944        assert_eq!(bytes, Bytes::from_static(&[0x01, 0x02, 0x00, 0x00]));
945    }
946
947    #[test]
948    fn test_parse_bgp_open_message() {
949        let bytes = Bytes::from_static(&[
950            0x04, // version
951            0x00, 0x01, // asn
952            0x00, 0xb4, // hold time
953            0xc0, 0x00, 0x02, 0x01, // sender ip
954            0x00, // opt params length
955        ]);
956        let msg = parse_bgp_open_message(&mut bytes.clone()).unwrap();
957        assert_eq!(msg.version, 4);
958        assert_eq!(msg.asn, Asn::new_16bit(1));
959        assert_eq!(msg.hold_time, 180);
960        assert_eq!(msg.bgp_identifier, Ipv4Addr::new(192, 0, 2, 1));
961        assert!(!msg.extended_length);
962        assert_eq!(msg.opt_params.len(), 0);
963    }
964
965    #[test]
966    fn test_encode_bgp_open_message() {
967        let msg = BgpOpenMessage {
968            version: 4,
969            asn: Asn::new_16bit(1),
970            hold_time: 180,
971            bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
972            extended_length: false,
973            opt_params: vec![],
974        };
975        let bytes = msg.encode();
976        assert_eq!(
977            bytes,
978            Bytes::from_static(&[
979                0x04, // version
980                0x00, 0x01, // asn
981                0x00, 0xb4, // hold time
982                0xc0, 0x00, 0x02, 0x01, // sender ip
983                0x00, // opt params length
984            ])
985        );
986    }
987
988    #[test]
989    fn test_encode_bgp_notification_message() {
990        let bgp_message = BgpMessage::Notification(BgpNotificationMessage {
991            error: BgpError::MessageHeaderError(MessageHeaderError::BAD_MESSAGE_LENGTH),
992            data: vec![0x00, 0x00],
993        });
994        let bytes = bgp_message.encode(AsnLength::Bits16);
995        // RFC 4271: Marker is 16 bytes of 0xFF
996        assert_eq!(
997            bytes,
998            Bytes::from_static(&[
999                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // marker (8 bytes)
1000                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // marker (8 bytes)
1001                0x00, 0x17, // length = 23 (16 marker + 2 len + 1 type + 4 msg)
1002                0x03, // type = NOTIFICATION
1003                0x01, 0x02, // error code, subcode
1004                0x00, 0x00 // data
1005            ])
1006        );
1007    }
1008
1009    #[test]
1010    fn test_bgp_message_from_bgp_update_message() {
1011        let msg = BgpMessage::from(BgpUpdateMessage::default());
1012        assert!(matches!(msg, BgpMessage::Update(_)));
1013    }
1014
1015    #[test]
1016    fn test_parse_bgp_open_message_with_extended_next_hop_capability() {
1017        use crate::models::{Afi, Safi};
1018
1019        // BGP OPEN message with Extended Next Hop capability - RFC 8950, Section 3
1020        // Version=4, ASN=65001, HoldTime=180, BGP-ID=192.0.2.1
1021        // One capability: Extended Next Hop (type=5) with two entries:
1022        // 1) IPv4 Unicast (AFI=1, SAFI=1) can use IPv6 NextHop (AFI=2)
1023        // 2) IPv4 MPLS VPN (AFI=1, SAFI=128) can use IPv6 NextHop (AFI=2)
1024        let bytes = Bytes::from(vec![
1025            0x04, // version
1026            0xfd, 0xe9, // asn = 65001
1027            0x00, 0xb4, // hold time = 180
1028            0xc0, 0x00, 0x02, 0x01, // sender ip = 192.0.2.1
1029            0x10, // opt params length = 16
1030            0x02, // param type = 2 (capability)
1031            0x0e, // param length = 14
1032            0x05, // capability type = 5 (Extended Next Hop)
1033            0x0c, // capability length = 12 (2 entries * 6 bytes each)
1034            0x00, 0x01, // NLRI AFI = 1 (IPv4)
1035            0x00, 0x01, // NLRI SAFI = 1 (Unicast)
1036            0x00, 0x02, // NextHop AFI = 2 (IPv6)
1037            0x00, 0x01, // NLRI AFI = 1 (IPv4) - second entry
1038            0x00, 0x80, // NLRI SAFI = 128 (MPLS VPN)
1039            0x00, 0x02, // NextHop AFI = 2 (IPv6)
1040        ]);
1041
1042        let msg = parse_bgp_open_message(&mut bytes.clone()).unwrap();
1043        assert_eq!(msg.version, 4);
1044        assert_eq!(msg.asn, Asn::new_16bit(65001));
1045        assert_eq!(msg.hold_time, 180);
1046        assert_eq!(msg.bgp_identifier, Ipv4Addr::new(192, 0, 2, 1));
1047        assert!(!msg.extended_length);
1048        assert_eq!(msg.opt_params.len(), 1);
1049
1050        // Check the capability
1051        if let ParamValue::Capacities(cap) = &msg.opt_params[0].param_value {
1052            assert_eq!(cap[0].ty, BgpCapabilityType::EXTENDED_NEXT_HOP_ENCODING);
1053
1054            if let CapabilityValue::ExtendedNextHop(enh_cap) = &cap[0].value {
1055                assert_eq!(enh_cap.entries.len(), 2);
1056
1057                // Check first entry: IPv4 Unicast can use IPv6 NextHop
1058                let entry1 = &enh_cap.entries[0];
1059                assert_eq!(entry1.nlri_afi, Afi::Ipv4);
1060                assert_eq!(entry1.nlri_safi, Safi::Unicast);
1061                assert_eq!(entry1.nexthop_afi, Afi::Ipv6);
1062
1063                // Check second entry: IPv4 MPLS VPN can use IPv6 NextHop
1064                let entry2 = &enh_cap.entries[1];
1065                assert_eq!(entry2.nlri_afi, Afi::Ipv4);
1066                assert_eq!(entry2.nlri_safi, Safi::MplsVpn);
1067                assert_eq!(entry2.nexthop_afi, Afi::Ipv6);
1068
1069                // Test functionality
1070                assert!(enh_cap.supports(Afi::Ipv4, Safi::Unicast, Afi::Ipv6));
1071                assert!(enh_cap.supports(Afi::Ipv4, Safi::MplsVpn, Afi::Ipv6));
1072                assert!(!enh_cap.supports(Afi::Ipv4, Safi::Multicast, Afi::Ipv6));
1073            } else {
1074                panic!("Expected ExtendedNextHop capability value");
1075            }
1076        } else {
1077            panic!("Expected capability parameter");
1078        }
1079    }
1080
1081    #[test]
1082    fn test_rfc8654_extended_message_length_validation() {
1083        // Test valid extended UPDATE message (within 65535 limit)
1084        let bytes = Bytes::from_static(&[
1085            0x00, 0x00, 0x00, 0x00, // marker
1086            0x00, 0x00, 0x00, 0x00, // marker
1087            0x00, 0x00, 0x00, 0x00, // marker
1088            0x00, 0x00, 0x00, 0x00, // marker
1089            0x13, 0x00, // length = 4864 (0x1300) (extended message)
1090            0x02, // type = UPDATE
1091            0x00, 0x00, // withdrawn length = 0
1092            0x00,
1093            0x00, // path attribute length = 0
1094                  // No NLRI data needed for this test
1095        ]);
1096        let mut data = bytes.clone();
1097        // This should succeed because UPDATE messages can be extended
1098        assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits16).is_ok());
1099
1100        // Test OPEN message exceeding 4096 bytes (should fail)
1101        let bytes = Bytes::from_static(&[
1102            0x00, 0x00, 0x00, 0x00, // marker
1103            0x00, 0x00, 0x00, 0x00, // marker
1104            0x00, 0x00, 0x00, 0x00, // marker
1105            0x00, 0x00, 0x00, 0x00, // marker
1106            0x13, 0x00, // length = 4864 (0x1300) (exceeds 4096 for OPEN)
1107            0x01, // type = OPEN
1108        ]);
1109        let mut data = bytes.clone();
1110        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1111        assert!(result.is_err());
1112        if let Err(ParserError::ParseError(msg)) = result {
1113            assert!(msg.contains("BGP OPEN message length"));
1114            assert!(msg.contains("4096 bytes"));
1115        }
1116
1117        // Test KEEPALIVE message exceeding 4096 bytes (should fail)
1118        let bytes = Bytes::from_static(&[
1119            0x00, 0x00, 0x00, 0x00, // marker
1120            0x00, 0x00, 0x00, 0x00, // marker
1121            0x00, 0x00, 0x00, 0x00, // marker
1122            0x00, 0x00, 0x00, 0x00, // marker
1123            0x13, 0x00, // length = 4864 (0x1300) (exceeds 4096 for KEEPALIVE)
1124            0x04, // type = KEEPALIVE
1125        ]);
1126        let mut data = bytes.clone();
1127        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1128        assert!(result.is_err());
1129        if let Err(ParserError::ParseError(msg)) = result {
1130            assert!(msg.contains("BGP KEEPALIVE message length"));
1131            assert!(msg.contains("4096 bytes"));
1132        }
1133
1134        // Test message exceeding 65535 bytes (maximum allowed)
1135        let bytes = Bytes::from_static(&[
1136            0x00, 0x00, 0x00, 0x00, // marker
1137            0x00, 0x00, 0x00, 0x00, // marker
1138            0x00, 0x00, 0x00, 0x00, // marker
1139            0x00, 0x00, 0x00, 0x00, // marker
1140            0xFF, 0xFF, // length = 65535 (0xFFFF) (maximum allowed)
1141            0x02, // type = UPDATE
1142        ]);
1143        let mut data = bytes.clone();
1144        // This might fail due to insufficient data, but should not fail on length validation
1145        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1146        if let Err(ParserError::ParseError(msg)) = result {
1147            // Should not be a length validation error
1148            assert!(!msg.contains("invalid BGP message length"));
1149        }
1150    }
1151
1152    #[test]
1153    fn test_bgp_extended_message_capability_parsing() {
1154        use crate::models::CapabilityValue;
1155
1156        // Test BGP OPEN message with Extended Message capability (capability code 6)
1157        let bytes = Bytes::from(vec![
1158            0x04, // version
1159            0x00, 0x01, // asn
1160            0x00, 0xb4, // hold time
1161            0xc0, 0x00, 0x02, 0x01, // sender ip
1162            0x04, // opt params length = 4
1163            0x02, // param type = 2 (capability)
1164            0x02, // param length = 2
1165            0x06, // capability type = 6 (Extended Message)
1166            0x00, // capability length = 0 (no parameters)
1167        ]);
1168
1169        let msg = parse_bgp_open_message(&mut bytes.clone()).unwrap();
1170        assert_eq!(msg.version, 4);
1171        assert_eq!(msg.asn, Asn::new_16bit(1));
1172        assert_eq!(msg.opt_params.len(), 1);
1173
1174        // Check that we have the extended message capability
1175        if let ParamValue::Capacities(cap) = &msg.opt_params[0].param_value {
1176            assert_eq!(cap[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
1177            if let CapabilityValue::BgpExtendedMessage(_) = &cap[0].value {
1178                // Extended Message capability should have no parameters
1179            } else {
1180                panic!("Expected BgpExtendedMessage capability value");
1181            }
1182        } else {
1183            panic!("Expected capability parameter");
1184        }
1185    }
1186
1187    #[test]
1188    fn test_rfc8654_edge_cases() {
1189        // Test NOTIFICATION message with extended length (should be allowed)
1190        let bytes = Bytes::from_static(&[
1191            0x00, 0x00, 0x00, 0x00, // marker
1192            0x00, 0x00, 0x00, 0x00, // marker
1193            0x00, 0x00, 0x00, 0x00, // marker
1194            0x00, 0x00, 0x00, 0x00, // marker
1195            0x20, 0x00, // length = 8192 (extended NOTIFICATION message)
1196            0x03, // type = NOTIFICATION
1197            0x06, // error code (Cease)
1198            0x00, // error subcode
1199                  // Additional data would go here
1200        ]);
1201        let mut data = bytes.clone();
1202        // This should succeed because NOTIFICATION messages can be extended
1203        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1204        // May fail due to insufficient data, but not due to length validation
1205        if let Err(ParserError::ParseError(msg)) = result {
1206            assert!(!msg.contains("invalid BGP message length"));
1207            assert!(!msg.contains("exceeds maximum allowed 4096 bytes"));
1208        }
1209
1210        // Test message exactly at 4096 bytes for OPEN (should be allowed)
1211        let open_data = vec![
1212            0x00, 0x00, 0x00, 0x00, // marker
1213            0x00, 0x00, 0x00, 0x00, // marker
1214            0x00, 0x00, 0x00, 0x00, // marker
1215            0x00, 0x00, 0x00, 0x00, // marker
1216            0x10, 0x00, // length = 4096 (exactly at limit for OPEN)
1217            0x01, // type = OPEN
1218        ];
1219        let bytes = Bytes::from(open_data);
1220        let mut data = bytes.clone();
1221        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1222        // Should not fail on length validation (may fail on parsing due to insufficient data)
1223        if let Err(ParserError::ParseError(msg)) = result {
1224            assert!(!msg.contains("exceeds maximum allowed 4096 bytes"));
1225        }
1226
1227        // Test message exactly at 65535 bytes for UPDATE (should be allowed)
1228        let bytes = Bytes::from_static(&[
1229            0x00, 0x00, 0x00, 0x00, // marker
1230            0x00, 0x00, 0x00, 0x00, // marker
1231            0x00, 0x00, 0x00, 0x00, // marker
1232            0x00, 0x00, 0x00, 0x00, // marker
1233            0xFF, 0xFF, // length = 65535 (0xFFFF) (maximum allowed)
1234            0x02, // type = UPDATE
1235        ]);
1236        let mut data = bytes.clone();
1237        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1238        // Should not fail on length validation
1239        if let Err(ParserError::ParseError(msg)) = result {
1240            assert!(!msg.contains("invalid BGP message length"));
1241        }
1242    }
1243
1244    #[test]
1245    fn test_rfc8654_capability_encoding_path() {
1246        use crate::models::capabilities::BgpExtendedMessageCapability;
1247
1248        // Test that the encoding path for BgpExtendedMessage capability is covered
1249        // This specifically tests the line: CapabilityValue::BgpExtendedMessage(bem) => bem.encode()
1250        let capability_value =
1251            CapabilityValue::BgpExtendedMessage(BgpExtendedMessageCapability::new());
1252        let capability = Capability {
1253            ty: BgpCapabilityType::BGP_EXTENDED_MESSAGE,
1254            value: capability_value,
1255        };
1256
1257        let opt_param = OptParam {
1258            param_type: 2, // capability
1259            param_len: 2,
1260            param_value: ParamValue::Capacities(vec![capability]),
1261        };
1262
1263        let msg = BgpOpenMessage {
1264            version: 4,
1265            asn: Asn::new_16bit(65001),
1266            hold_time: 180,
1267            bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
1268            extended_length: false,
1269            opt_params: vec![opt_param],
1270        };
1271
1272        // This will exercise the encoding path we need to test
1273        let encoded = msg.encode();
1274        assert!(!encoded.is_empty());
1275
1276        // Verify we can parse it back (exercises the parsing path too)
1277        let parsed = parse_bgp_open_message(&mut encoded.clone()).unwrap();
1278        assert_eq!(parsed.opt_params.len(), 1);
1279        if let ParamValue::Capacities(cap) = &parsed.opt_params[0].param_value {
1280            assert_eq!(cap[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
1281        }
1282    }
1283
1284    #[test]
1285    fn test_rfc8654_error_message_formatting() {
1286        // Test the error message formatting paths that include message type names
1287        // This tests the match arms for OPEN and KEEPALIVE in error messages
1288
1289        // Test OPEN message error path
1290        let bytes = Bytes::from_static(&[
1291            0x00, 0x00, 0x00, 0x00, // marker
1292            0x00, 0x00, 0x00, 0x00, // marker
1293            0x00, 0x00, 0x00, 0x00, // marker
1294            0x00, 0x00, 0x00, 0x00, // marker
1295            0x20, 0x01, // length = 8193 (exceeds 4096 for OPEN)
1296            0x01, // type = OPEN
1297        ]);
1298        let mut data = bytes.clone();
1299        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1300        assert!(result.is_err());
1301        if let Err(ParserError::ParseError(msg)) = result {
1302            assert!(msg.contains("BGP OPEN message length"));
1303            assert!(msg.contains("exceeds maximum allowed 4096 bytes"));
1304        }
1305
1306        // Test KEEPALIVE message error path
1307        let bytes = Bytes::from_static(&[
1308            0x00, 0x00, 0x00, 0x00, // marker
1309            0x00, 0x00, 0x00, 0x00, // marker
1310            0x00, 0x00, 0x00, 0x00, // marker
1311            0x00, 0x00, 0x00, 0x00, // marker
1312            0x20, 0x01, // length = 8193 (exceeds 4096 for KEEPALIVE)
1313            0x04, // type = KEEPALIVE
1314        ]);
1315        let mut data = bytes.clone();
1316        let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
1317        assert!(result.is_err());
1318        if let Err(ParserError::ParseError(msg)) = result {
1319            assert!(msg.contains("BGP KEEPALIVE message length"));
1320            assert!(msg.contains("exceeds maximum allowed 4096 bytes"));
1321        }
1322    }
1323
1324    #[test]
1325    fn test_encode_bgp_open_message_with_extended_message_capability() {
1326        use crate::models::capabilities::BgpExtendedMessageCapability;
1327
1328        // Create Extended Message capability
1329        let extended_msg_capability = BgpExtendedMessageCapability::new();
1330
1331        let msg = BgpOpenMessage {
1332            version: 4,
1333            asn: Asn::new_16bit(65001),
1334            hold_time: 180,
1335            bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
1336            extended_length: false,
1337            opt_params: vec![OptParam {
1338                param_type: 2, // capability
1339                param_len: 2,  // 1 (type) + 1 (len) + 0 (no parameters)
1340                param_value: ParamValue::Capacities(vec![Capability {
1341                    ty: BgpCapabilityType::BGP_EXTENDED_MESSAGE,
1342                    value: CapabilityValue::BgpExtendedMessage(extended_msg_capability),
1343                }]),
1344            }],
1345        };
1346
1347        let encoded = msg.encode();
1348
1349        // Parse the encoded message back and verify it matches
1350        let parsed = parse_bgp_open_message(&mut encoded.clone()).unwrap();
1351        assert_eq!(parsed.version, msg.version);
1352        assert_eq!(parsed.asn, msg.asn);
1353        assert_eq!(parsed.hold_time, msg.hold_time);
1354        assert_eq!(parsed.bgp_identifier, msg.bgp_identifier);
1355        assert_eq!(parsed.opt_params.len(), 1);
1356
1357        // Verify the capability was encoded and parsed correctly
1358        if let ParamValue::Capacities(cap) = &parsed.opt_params[0].param_value {
1359            assert_eq!(cap[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
1360            if let CapabilityValue::BgpExtendedMessage(_) = &cap[0].value {
1361                // Extended Message capability should have no parameters
1362            } else {
1363                panic!("Expected BgpExtendedMessage capability value after round trip");
1364            }
1365        } else {
1366            panic!("Expected capability parameter after round trip");
1367        }
1368    }
1369
1370    #[test]
1371    fn test_encode_bgp_open_message_with_extended_next_hop_capability() {
1372        use crate::models::capabilities::{ExtendedNextHopCapability, ExtendedNextHopEntry};
1373        use crate::models::{Afi, Safi};
1374
1375        // Create Extended Next Hop capability
1376        let entries = vec![
1377            ExtendedNextHopEntry {
1378                nlri_afi: Afi::Ipv4,
1379                nlri_safi: Safi::Unicast,
1380                nexthop_afi: Afi::Ipv6,
1381            },
1382            ExtendedNextHopEntry {
1383                nlri_afi: Afi::Ipv4,
1384                nlri_safi: Safi::MplsVpn,
1385                nexthop_afi: Afi::Ipv6,
1386            },
1387        ];
1388        let enh_capability = ExtendedNextHopCapability::new(entries);
1389
1390        let msg = BgpOpenMessage {
1391            version: 4,
1392            asn: Asn::new_16bit(65001),
1393            hold_time: 180,
1394            bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
1395            extended_length: false,
1396            opt_params: vec![OptParam {
1397                param_type: 2, // capability
1398                param_len: 14, // 1 (type) + 1 (len) + 12 (2 entries * 6 bytes)
1399                param_value: ParamValue::Capacities(vec![Capability {
1400                    ty: BgpCapabilityType::EXTENDED_NEXT_HOP_ENCODING,
1401                    value: CapabilityValue::ExtendedNextHop(enh_capability),
1402                }]),
1403            }],
1404        };
1405
1406        let encoded = msg.encode();
1407
1408        // Parse the encoded message back and verify it matches
1409        let parsed = parse_bgp_open_message(&mut encoded.clone()).unwrap();
1410        assert_eq!(parsed.version, msg.version);
1411        assert_eq!(parsed.asn, msg.asn);
1412        assert_eq!(parsed.hold_time, msg.hold_time);
1413        assert_eq!(parsed.bgp_identifier, msg.bgp_identifier);
1414        assert_eq!(parsed.extended_length, msg.extended_length);
1415        assert_eq!(parsed.opt_params.len(), 1);
1416
1417        // Verify the capability was encoded and parsed correctly
1418        if let ParamValue::Capacities(cap) = &parsed.opt_params[0].param_value {
1419            assert_eq!(cap[0].ty, BgpCapabilityType::EXTENDED_NEXT_HOP_ENCODING);
1420            if let CapabilityValue::ExtendedNextHop(enh_cap) = &cap[0].value {
1421                assert_eq!(enh_cap.entries.len(), 2);
1422                assert!(enh_cap.supports(Afi::Ipv4, Safi::Unicast, Afi::Ipv6));
1423                assert!(enh_cap.supports(Afi::Ipv4, Safi::MplsVpn, Afi::Ipv6));
1424            } else {
1425                panic!("Expected ExtendedNextHop capability value after round trip");
1426            }
1427        } else {
1428            panic!("Expected capability parameter after round trip");
1429        }
1430    }
1431
1432    #[test]
1433    fn test_parse_bgp_open_message_with_multiple_capabilities() {
1434        // Create a BGP OPEN message with multiple capabilities in a single optional parameter
1435        // This tests RFC 5492 support for multiple capabilities per parameter
1436
1437        // Build capabilities: Extended Message, Route Refresh, and 4-octet AS
1438        let extended_msg_cap = Capability {
1439            ty: BgpCapabilityType::BGP_EXTENDED_MESSAGE,
1440            value: CapabilityValue::BgpExtendedMessage(BgpExtendedMessageCapability {}),
1441        };
1442
1443        let route_refresh_cap = Capability {
1444            ty: BgpCapabilityType::ROUTE_REFRESH_CAPABILITY_FOR_BGP_4,
1445            value: CapabilityValue::RouteRefresh(RouteRefreshCapability {}),
1446        };
1447
1448        let four_octet_as_cap = Capability {
1449            ty: BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY,
1450            value: CapabilityValue::FourOctetAs(FourOctetAsCapability { asn: 65536 }),
1451        };
1452
1453        // Create OPEN message with all three capabilities in one parameter
1454        let msg = BgpOpenMessage {
1455            version: 4,
1456            asn: Asn::new_32bit(65000),
1457            hold_time: 180,
1458            bgp_identifier: "10.0.0.1".parse().unwrap(),
1459            extended_length: false,
1460            opt_params: vec![OptParam {
1461                param_type: 2, // capability
1462                param_len: 10, // 3 capabilities: (1+1+0) + (1+1+0) + (1+1+4) = 2+2+6 = 10
1463                param_value: ParamValue::Capacities(vec![
1464                    extended_msg_cap,
1465                    route_refresh_cap,
1466                    four_octet_as_cap,
1467                ]),
1468            }],
1469        };
1470
1471        // Encode the message
1472        let encoded = msg.encode();
1473
1474        // Parse it back
1475        let mut encoded_bytes = encoded.clone();
1476        let parsed = parse_bgp_open_message(&mut encoded_bytes).unwrap();
1477
1478        // Verify basic fields
1479        assert_eq!(parsed.version, 4);
1480        assert_eq!(parsed.asn, Asn::new_32bit(65000));
1481        assert_eq!(parsed.hold_time, 180);
1482        assert_eq!(
1483            parsed.bgp_identifier,
1484            "10.0.0.1".parse::<std::net::Ipv4Addr>().unwrap()
1485        );
1486        assert_eq!(parsed.opt_params.len(), 1);
1487
1488        // Verify we have all three capabilities
1489        if let ParamValue::Capacities(caps) = &parsed.opt_params[0].param_value {
1490            assert_eq!(caps.len(), 3, "Should have 3 capabilities");
1491
1492            // Check first capability: Extended Message
1493            assert_eq!(caps[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
1494            assert!(matches!(
1495                caps[0].value,
1496                CapabilityValue::BgpExtendedMessage(_)
1497            ));
1498
1499            // Check second capability: Route Refresh
1500            assert_eq!(
1501                caps[1].ty,
1502                BgpCapabilityType::ROUTE_REFRESH_CAPABILITY_FOR_BGP_4
1503            );
1504            assert!(matches!(caps[1].value, CapabilityValue::RouteRefresh(_)));
1505
1506            // Check third capability: 4-octet AS
1507            assert_eq!(
1508                caps[2].ty,
1509                BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY
1510            );
1511            if let CapabilityValue::FourOctetAs(foa) = &caps[2].value {
1512                assert_eq!(foa.asn, 65536);
1513            } else {
1514                panic!("Expected FourOctetAs capability value");
1515            }
1516        } else {
1517            panic!("Expected Capacities parameter");
1518        }
1519    }
1520
1521    #[test]
1522    fn test_parse_bgp_open_message_with_multiple_capability_parameters() {
1523        // Test parsing OPEN message with multiple optional parameters, each containing capabilities
1524        // This is less common but still valid per RFC 5492
1525
1526        let msg = BgpOpenMessage {
1527            version: 4,
1528            asn: Asn::new_32bit(65001),
1529            hold_time: 90,
1530            bgp_identifier: "192.168.1.1".parse().unwrap(),
1531            extended_length: false,
1532            opt_params: vec![
1533                OptParam {
1534                    param_type: 2, // capability
1535                    param_len: 2,
1536                    param_value: ParamValue::Capacities(vec![Capability {
1537                        ty: BgpCapabilityType::BGP_EXTENDED_MESSAGE,
1538                        value: CapabilityValue::BgpExtendedMessage(BgpExtendedMessageCapability {}),
1539                    }]),
1540                },
1541                OptParam {
1542                    param_type: 2, // capability
1543                    param_len: 6,
1544                    param_value: ParamValue::Capacities(vec![Capability {
1545                        ty: BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY,
1546                        value: CapabilityValue::FourOctetAs(FourOctetAsCapability {
1547                            asn: 4200000000,
1548                        }),
1549                    }]),
1550                },
1551            ],
1552        };
1553
1554        // Encode and parse back
1555        let encoded = msg.encode();
1556        let mut encoded_bytes = encoded.clone();
1557        let parsed = parse_bgp_open_message(&mut encoded_bytes).unwrap();
1558
1559        // Verify we have 2 optional parameters
1560        assert_eq!(parsed.opt_params.len(), 2);
1561
1562        // Check first parameter
1563        if let ParamValue::Capacities(caps) = &parsed.opt_params[0].param_value {
1564            assert_eq!(caps.len(), 1);
1565            assert_eq!(caps[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
1566        } else {
1567            panic!("Expected Capacities in first parameter");
1568        }
1569
1570        // Check second parameter
1571        if let ParamValue::Capacities(caps) = &parsed.opt_params[1].param_value {
1572            assert_eq!(caps.len(), 1);
1573            assert_eq!(
1574                caps[0].ty,
1575                BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY
1576            );
1577            if let CapabilityValue::FourOctetAs(foa) = &caps[0].value {
1578                assert_eq!(foa.asn, 4200000000);
1579            }
1580        } else {
1581            panic!("Expected Capacities in second parameter");
1582        }
1583    }
1584}