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