bgpkit_parser/parser/bgp/
messages.rs

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