Skip to main content

bgpkit_parser/parser/bgp/
messages.rs

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