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::BgpCapabilityType;
7use crate::models::error::BgpError;
8use crate::parser::bgp::attributes::parse_attributes;
9use crate::parser::{encode_ipaddr, encode_nlri_prefixes, parse_nlri_list, ReadUtils};
10use log::warn;
11
12/// BGP message
13///
14/// Format:
15/// ```text
16/// 0                   1                   2                   3
17/// 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
18/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19/// |                                                               |
20/// +                                                               +
21/// |                                                               |
22/// +                                                               +
23/// |                           Marker                              |
24/// +                                                               +
25/// |                                                               |
26/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
27/// |          Length               |      Type     |
28/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
29/// ```
30pub fn parse_bgp_message(
31    data: &mut Bytes,
32    add_path: bool,
33    asn_len: &AsnLength,
34) -> Result<BgpMessage, ParserError> {
35    let total_size = data.len();
36    data.has_n_remaining(19)?;
37    data.advance(16);
38    /*
39    This 2-octet unsigned integer indicates the total length of the
40    message, including the header in octets.  Thus, it allows one
41    to locate the (Marker field of the) next message in the TCP
42    stream.  The value of the Length field MUST always be at least
43    19 and no greater than 4096, and MAY be further constrained,
44    depending on the message type.  "padding" of extra data after
45    the message is not allowed.  Therefore, the Length field MUST
46    have the smallest value required, given the rest of the
47    message.
48    */
49    let length = data.get_u16();
50    if !(19..=4096).contains(&length) {
51        return Err(ParserError::ParseError(format!(
52            "invalid BGP message length {}",
53            length
54        )));
55    }
56
57    let bgp_msg_length = if (length as usize) > total_size {
58        total_size - 19
59    } else {
60        length as usize - 19
61    };
62
63    let msg_type: BgpMessageType = match BgpMessageType::try_from(data.get_u8()) {
64        Ok(t) => t,
65        Err(_) => {
66            return Err(ParserError::ParseError(
67                "Unknown BGP Message Type".to_string(),
68            ))
69        }
70    };
71
72    if data.remaining() != bgp_msg_length {
73        warn!(
74            "BGP message length {} does not match the actual length {}",
75            bgp_msg_length,
76            data.remaining()
77        );
78    }
79    data.has_n_remaining(bgp_msg_length)?;
80    let mut msg_data = data.split_to(bgp_msg_length);
81
82    Ok(match msg_type {
83        BgpMessageType::OPEN => BgpMessage::Open(parse_bgp_open_message(&mut msg_data)?),
84        BgpMessageType::UPDATE => {
85            BgpMessage::Update(parse_bgp_update_message(msg_data, add_path, asn_len)?)
86        }
87        BgpMessageType::NOTIFICATION => {
88            BgpMessage::Notification(parse_bgp_notification_message(msg_data)?)
89        }
90        BgpMessageType::KEEPALIVE => BgpMessage::KeepAlive,
91    })
92}
93
94/// Parse BGP NOTIFICATION message.
95///
96/// The BGP NOTIFICATION messages contains BGP error codes received from a connected BGP router. The
97/// error code is parsed into [BgpError] data structure and any unknown codes will produce warning
98/// messages, but not critical errors.
99///
100pub fn parse_bgp_notification_message(
101    mut input: Bytes,
102) -> Result<BgpNotificationMessage, ParserError> {
103    let error_code = input.read_u8()?;
104    let error_subcode = input.read_u8()?;
105
106    Ok(BgpNotificationMessage {
107        error: BgpError::new(error_code, error_subcode),
108        data: input.read_n_bytes(input.len())?,
109    })
110}
111
112impl BgpNotificationMessage {
113    pub fn encode(&self) -> Bytes {
114        let mut buf = BytesMut::new();
115        let (code, subcode) = self.error.get_codes();
116        buf.put_u8(code);
117        buf.put_u8(subcode);
118        buf.put_slice(&self.data);
119        buf.freeze()
120    }
121}
122
123/// Parse BGP OPEN message.
124///
125/// The parsing of BGP OPEN message also includes decoding the BGP capabilities.
126pub fn parse_bgp_open_message(input: &mut Bytes) -> Result<BgpOpenMessage, ParserError> {
127    input.has_n_remaining(10)?;
128    let version = input.get_u8();
129    let asn = Asn::new_16bit(input.get_u16());
130    let hold_time = input.get_u16();
131
132    let sender_ip = input.read_ipv4_address()?;
133    let mut opt_params_len: u16 = input.get_u8() as u16;
134
135    let mut extended_length = false;
136    let mut first = true;
137
138    let mut params: Vec<OptParam> = vec![];
139    while input.remaining() >= 2 {
140        let mut param_type = input.get_u8();
141        if first {
142            // first parameter, check if it is extended length message
143            if opt_params_len == 255 && param_type == 255 {
144                //
145                // 0                   1                   2                   3
146                // 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
147                //     +-+-+-+-+-+-+-+-+
148                //     |    Version    |
149                //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
150                //     |     My Autonomous System      |
151                //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
152                //     |           Hold Time           |
153                //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
154                //     |                         BGP Identifier                        |
155                //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
156                //     |Non-Ext OP Len.|Non-Ext OP Type|  Extended Opt. Parm. Length   |
157                //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
158                //     |                                                               |
159                //     |             Optional Parameters (variable)                    |
160                //     |                                                               |
161                //         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
162                //
163                //         Figure 1: Extended Encoding OPEN Format
164                extended_length = true;
165                opt_params_len = input.read_u16()?;
166                if opt_params_len == 0 {
167                    break;
168                }
169                // let pos_end = input.position() + opt_params_len as u64;
170                if input.remaining() != opt_params_len as usize {
171                    warn!(
172                        "BGP open message length {} does not match the actual length {}",
173                        opt_params_len,
174                        input.remaining()
175                    );
176                }
177
178                param_type = input.read_u8()?;
179            }
180            first = false;
181        }
182        // reaching here means all the remain params are regular non-extended-length parameters
183
184        let param_len = match extended_length {
185            true => input.read_u16()?,
186            false => input.read_u8()? as u16,
187        };
188        // https://tools.ietf.org/html/rfc3392
189        // https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-11
190
191        let param_value = match param_type {
192            2 => {
193                // capability codes:
194                // https://www.iana.org/assignments/capability-codes/capability-codes.xhtml#capability-codes-2
195                let code = input.read_u8()?;
196                let len = match extended_length {
197                    true => input.read_u16()?,
198                    false => input.read_u8()? as u16,
199                };
200
201                ParamValue::Capability(Capability {
202                    ty: BgpCapabilityType::from(code),
203                    value: input.read_n_bytes(len as usize)?,
204                })
205            }
206            _ => {
207                // unsupported param, read as raw bytes
208                let bytes = input.read_n_bytes(param_len as usize)?;
209                ParamValue::Raw(bytes)
210            }
211        };
212        params.push(OptParam {
213            param_type,
214            param_len,
215            param_value,
216        });
217    }
218
219    Ok(BgpOpenMessage {
220        version,
221        asn,
222        hold_time,
223        sender_ip,
224        extended_length,
225        opt_params: params,
226    })
227}
228
229impl BgpOpenMessage {
230    pub fn encode(&self) -> Bytes {
231        let mut buf = BytesMut::new();
232        buf.put_u8(self.version);
233        buf.put_u16(self.asn.into());
234        buf.put_u16(self.hold_time);
235        buf.extend(encode_ipaddr(&self.sender_ip.into()));
236        buf.put_u8(self.opt_params.len() as u8);
237        for param in &self.opt_params {
238            buf.put_u8(param.param_type);
239            buf.put_u8(param.param_len as u8);
240            match &param.param_value {
241                ParamValue::Capability(cap) => {
242                    buf.put_u8(cap.ty.into());
243                    buf.put_u8(cap.value.len() as u8);
244                    buf.extend(&cap.value);
245                }
246                ParamValue::Raw(bytes) => {
247                    buf.extend(bytes);
248                }
249            }
250        }
251        buf.freeze()
252    }
253}
254
255/// read nlri portion of a bgp update message.
256fn read_nlri(
257    mut input: Bytes,
258    afi: &Afi,
259    add_path: bool,
260) -> Result<Vec<NetworkPrefix>, ParserError> {
261    let length = input.len();
262    if length == 0 {
263        return Ok(vec![]);
264    }
265    if length == 1 {
266        // 1 byte does not make sense
267        warn!("seeing strange one-byte NLRI field");
268        input.advance(1); // skip the byte
269        return Ok(vec![]);
270    }
271
272    parse_nlri_list(input, add_path, afi)
273}
274
275/// read bgp update message.
276///
277/// RFC: <https://tools.ietf.org/html/rfc4271#section-4.3>
278pub fn parse_bgp_update_message(
279    mut input: Bytes,
280    add_path: bool,
281    asn_len: &AsnLength,
282) -> Result<BgpUpdateMessage, ParserError> {
283    // NOTE: AFI for routes outside attributes are IPv4 ONLY.
284    let afi = Afi::Ipv4;
285
286    // parse withdrawn prefixes NLRI
287    let withdrawn_bytes_length = input.read_u16()? as usize;
288    input.has_n_remaining(withdrawn_bytes_length)?;
289    let withdrawn_bytes = input.split_to(withdrawn_bytes_length);
290    let withdrawn_prefixes = read_nlri(withdrawn_bytes, &afi, add_path)?;
291
292    // parse attributes
293    let attribute_length = input.read_u16()? as usize;
294
295    input.has_n_remaining(attribute_length)?;
296    let attr_data_slice = input.split_to(attribute_length);
297    let attributes = parse_attributes(attr_data_slice, asn_len, add_path, None, None, None)?;
298
299    // parse announced prefixes nlri.
300    // the remaining bytes are announced prefixes.
301    let announced_prefixes = read_nlri(input, &afi, add_path)?;
302
303    Ok(BgpUpdateMessage {
304        withdrawn_prefixes,
305        attributes,
306        announced_prefixes,
307    })
308}
309
310impl BgpUpdateMessage {
311    pub fn encode(&self, add_path: bool, asn_len: AsnLength) -> Bytes {
312        let mut bytes = BytesMut::new();
313
314        // withdrawn prefixes
315        let withdrawn_bytes = encode_nlri_prefixes(&self.withdrawn_prefixes, add_path);
316        bytes.put_u16(withdrawn_bytes.len() as u16);
317        bytes.put_slice(&withdrawn_bytes);
318
319        // attributes
320        let attr_bytes = self.attributes.encode(add_path, asn_len);
321
322        bytes.put_u16(attr_bytes.len() as u16);
323        bytes.put_slice(&attr_bytes);
324
325        bytes.extend(encode_nlri_prefixes(&self.announced_prefixes, add_path));
326        bytes.freeze()
327    }
328
329    /// Check if this is an end-of-rib message.
330    ///
331    /// <https://datatracker.ietf.org/doc/html/rfc4724#section-2>
332    /// End-of-rib message is a special update message that contains no NLRI or withdrawal NLRI prefixes.
333    pub fn is_end_of_rib(&self) -> bool {
334        // there are two cases for end-of-rib message:
335        // 1. IPv4 unicast address family: no announced, no withdrawn, no attributes
336        // 2. Other cases: no announced, no withdrawal, only MP_UNREACH_NRLI with no prefixes
337
338        if !self.announced_prefixes.is_empty() || !self.withdrawn_prefixes.is_empty() {
339            // has announced or withdrawal IPv4 unicast prefixes:
340            // definitely not end-of-rib
341
342            return false;
343        }
344
345        if self.attributes.inner.is_empty() {
346            // no attributes, no prefixes:
347            // case 1 end-of-rib
348            return true;
349        }
350
351        // has some attributes, it can only be withdrawal with no prefixes
352
353        if self.attributes.inner.len() > 1 {
354            // has more than one attributes, not end-of-rib
355            return false;
356        }
357
358        // has only one attribute, check if it is withdrawal attribute
359        if let AttributeValue::MpUnreachNlri(nlri) = &self.attributes.inner.first().unwrap().value {
360            if nlri.prefixes.is_empty() {
361                // the only attribute is MP_UNREACH_NLRI with no prefixes:
362                // case 2 end-of-rib
363                return true;
364            }
365        }
366
367        // all other cases: not end-of-rib
368        false
369    }
370}
371
372impl BgpMessage {
373    pub fn encode(&self, add_path: bool, asn_len: AsnLength) -> Bytes {
374        let mut bytes = BytesMut::new();
375        bytes.put_u32(0); // marker
376        bytes.put_u32(0); // marker
377        bytes.put_u32(0); // marker
378        bytes.put_u32(0); // marker
379
380        let (msg_type, msg_bytes) = match self {
381            BgpMessage::Open(msg) => (BgpMessageType::OPEN, msg.encode()),
382            BgpMessage::Update(msg) => (BgpMessageType::UPDATE, msg.encode(add_path, asn_len)),
383            BgpMessage::Notification(msg) => (BgpMessageType::NOTIFICATION, msg.encode()),
384            BgpMessage::KeepAlive => (BgpMessageType::KEEPALIVE, Bytes::new()),
385        };
386
387        // msg total bytes length = msg bytes + 16 bytes marker + 2 bytes length + 1 byte type
388        bytes.put_u16(msg_bytes.len() as u16 + 16 + 2 + 1);
389        bytes.put_u8(msg_type as u8);
390        bytes.put_slice(&msg_bytes);
391        bytes.freeze()
392    }
393}
394
395impl From<&BgpElem> for BgpUpdateMessage {
396    fn from(elem: &BgpElem) -> Self {
397        BgpUpdateMessage {
398            withdrawn_prefixes: vec![],
399            attributes: Attributes::from(elem),
400            announced_prefixes: vec![],
401        }
402    }
403}
404
405impl From<BgpUpdateMessage> for BgpMessage {
406    fn from(value: BgpUpdateMessage) -> Self {
407        BgpMessage::Update(value)
408    }
409}
410
411#[cfg(test)]
412mod tests {
413    use super::*;
414    use std::net::Ipv4Addr;
415    use std::str::FromStr;
416
417    #[test]
418    fn test_end_of_rib() {
419        // No prefixes and empty attributes: end-of-rib
420        let attrs = Attributes::default();
421        let msg = BgpUpdateMessage {
422            withdrawn_prefixes: vec![],
423            attributes: attrs,
424            announced_prefixes: vec![],
425        };
426        assert!(msg.is_end_of_rib());
427
428        // single MP_UNREACH_NLRI attribute with no prefixes: end-of-rib
429        let attrs = Attributes::from_iter(vec![AttributeValue::MpUnreachNlri(Nlri {
430            afi: Afi::Ipv4,
431            safi: Safi::Unicast,
432            next_hop: None,
433            prefixes: vec![],
434        })]);
435        let msg = BgpUpdateMessage {
436            withdrawn_prefixes: vec![],
437            attributes: attrs,
438            announced_prefixes: vec![],
439        };
440        assert!(msg.is_end_of_rib());
441
442        // message with announced prefixes
443        let prefix = NetworkPrefix::from_str("192.168.1.0/24").unwrap();
444        let attrs = Attributes::default();
445        let msg = BgpUpdateMessage {
446            withdrawn_prefixes: vec![],
447            attributes: attrs,
448            announced_prefixes: vec![prefix],
449        };
450        assert!(!msg.is_end_of_rib());
451
452        // message with withdrawn prefixes
453        let prefix = NetworkPrefix::from_str("192.168.1.0/24").unwrap();
454        let attrs = Attributes::default();
455        let msg = BgpUpdateMessage {
456            withdrawn_prefixes: vec![prefix],
457            attributes: attrs,
458            announced_prefixes: vec![],
459        };
460        assert!(!msg.is_end_of_rib());
461
462        // NLRI attribute with empty prefixes: NOT end-of-rib
463        let attrs = Attributes::from_iter(vec![AttributeValue::MpReachNlri(Nlri {
464            afi: Afi::Ipv4,
465            safi: Safi::Unicast,
466            next_hop: None,
467            prefixes: vec![],
468        })]);
469        let msg = BgpUpdateMessage {
470            withdrawn_prefixes: vec![],
471            attributes: attrs,
472            announced_prefixes: vec![],
473        };
474        assert!(!msg.is_end_of_rib());
475
476        // NLRI attribute with non-empty prefixes
477        let attrs = Attributes::from_iter(vec![AttributeValue::MpReachNlri(Nlri {
478            afi: Afi::Ipv4,
479            safi: Safi::Unicast,
480            next_hop: None,
481            prefixes: vec![prefix],
482        })]);
483        let msg = BgpUpdateMessage {
484            withdrawn_prefixes: vec![],
485            attributes: attrs,
486            announced_prefixes: vec![],
487        };
488        assert!(!msg.is_end_of_rib());
489
490        // Unreachable NLRI attribute with non-empty prefixes
491        let attrs = Attributes::from_iter(vec![AttributeValue::MpUnreachNlri(Nlri {
492            afi: Afi::Ipv4,
493            safi: Safi::Unicast,
494            next_hop: None,
495            prefixes: vec![prefix],
496        })]);
497        let msg = BgpUpdateMessage {
498            withdrawn_prefixes: vec![],
499            attributes: attrs,
500            announced_prefixes: vec![],
501        };
502        assert!(!msg.is_end_of_rib());
503
504        // message with more than one attributes
505        let attrs = Attributes::from_iter(vec![
506            AttributeValue::MpUnreachNlri(Nlri {
507                afi: Afi::Ipv4,
508                safi: Safi::Unicast,
509                next_hop: None,
510                prefixes: vec![],
511            }),
512            AttributeValue::AtomicAggregate,
513        ]);
514        let msg = BgpUpdateMessage {
515            withdrawn_prefixes: vec![],
516            attributes: attrs,
517            announced_prefixes: vec![],
518        };
519        assert!(!msg.is_end_of_rib());
520    }
521
522    #[test]
523    fn test_invlaid_length() {
524        let bytes = Bytes::from_static(&[
525            0x00, 0x00, 0x00, 0x00, // marker
526            0x00, 0x00, 0x00, 0x00, // marker
527            0x00, 0x00, 0x00, 0x00, // marker
528            0x00, 0x00, 0x00, 0x00, // marker
529            0x00, 0x00, // length
530            0x05, // type
531        ]);
532        let mut data = bytes.clone();
533        assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits16).is_err());
534
535        let bytes = Bytes::from_static(&[
536            0x00, 0x00, 0x00, 0x00, // marker
537            0x00, 0x00, 0x00, 0x00, // marker
538            0x00, 0x00, 0x00, 0x00, // marker
539            0x00, 0x00, 0x00, 0x00, // marker
540            0x00, 0x28, // length
541            0x05, // type
542        ]);
543        let mut data = bytes.clone();
544        assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits16).is_err());
545    }
546
547    #[test]
548    fn test_invlaid_type() {
549        let bytes = Bytes::from_static(&[
550            0x00, 0x00, 0x00, 0x00, // marker
551            0x00, 0x00, 0x00, 0x00, // marker
552            0x00, 0x00, 0x00, 0x00, // marker
553            0x00, 0x00, 0x00, 0x00, // marker
554            0x00, 0x28, // length
555            0x05, // type
556        ]);
557        let mut data = bytes.clone();
558        assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits16).is_err());
559    }
560
561    #[test]
562    fn test_parse_bgp_notification_message() {
563        let bytes = Bytes::from_static(&[
564            0x01, // error code
565            0x02, // error subcode
566            0x00, 0x00, // data
567        ]);
568        let msg = parse_bgp_notification_message(bytes).unwrap();
569        matches!(
570            msg.error,
571            BgpError::MessageHeaderError(MessageHeaderError::BAD_MESSAGE_LENGTH)
572        );
573        assert_eq!(msg.data, Bytes::from_static(&[0x00, 0x00]));
574    }
575
576    #[test]
577    fn test_encode_bgp_notification_messsage() {
578        let msg = BgpNotificationMessage {
579            error: BgpError::MessageHeaderError(MessageHeaderError::BAD_MESSAGE_LENGTH),
580            data: vec![0x00, 0x00],
581        };
582        let bytes = msg.encode();
583        assert_eq!(bytes, Bytes::from_static(&[0x01, 0x02, 0x00, 0x00]));
584    }
585
586    #[test]
587    fn test_parse_bgp_open_message() {
588        let bytes = Bytes::from_static(&[
589            0x04, // version
590            0x00, 0x01, // asn
591            0x00, 0xb4, // hold time
592            0xc0, 0x00, 0x02, 0x01, // sender ip
593            0x00, // opt params length
594        ]);
595        let msg = parse_bgp_open_message(&mut bytes.clone()).unwrap();
596        assert_eq!(msg.version, 4);
597        assert_eq!(msg.asn, Asn::new_16bit(1));
598        assert_eq!(msg.hold_time, 180);
599        assert_eq!(msg.sender_ip, Ipv4Addr::new(192, 0, 2, 1));
600        assert!(!msg.extended_length);
601        assert_eq!(msg.opt_params.len(), 0);
602    }
603
604    #[test]
605    fn test_encode_bgp_open_message() {
606        let msg = BgpOpenMessage {
607            version: 4,
608            asn: Asn::new_16bit(1),
609            hold_time: 180,
610            sender_ip: Ipv4Addr::new(192, 0, 2, 1),
611            extended_length: false,
612            opt_params: vec![],
613        };
614        let bytes = msg.encode();
615        assert_eq!(
616            bytes,
617            Bytes::from_static(&[
618                0x04, // version
619                0x00, 0x01, // asn
620                0x00, 0xb4, // hold time
621                0xc0, 0x00, 0x02, 0x01, // sender ip
622                0x00, // opt params length
623            ])
624        );
625    }
626
627    #[test]
628    fn test_encode_bgp_notification_message() {
629        let bgp_message = BgpMessage::Notification(BgpNotificationMessage {
630            error: BgpError::MessageHeaderError(MessageHeaderError::BAD_MESSAGE_LENGTH),
631            data: vec![0x00, 0x00],
632        });
633        let bytes = bgp_message.encode(false, AsnLength::Bits16);
634        assert_eq!(
635            bytes,
636            Bytes::from_static(&[
637                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
638                0x00, 0x00, 0x00, 0x17, 0x03, 0x01, 0x02, 0x00, 0x00
639            ])
640        );
641    }
642
643    #[test]
644    fn test_bgp_message_from_bgp_update_message() {
645        let msg = BgpMessage::from(BgpUpdateMessage::default());
646        assert!(matches!(msg, BgpMessage::Update(_)));
647    }
648}