bgp_rs/update/
nlri.rs

1use byteorder::{BigEndian, ReadBytesExt};
2
3use std::convert::TryFrom;
4use std::io::{Cursor, Error, ErrorKind, Read};
5
6use crate::*;
7
8/// Used when announcing routes to non-IPv4 addresses.
9#[derive(Debug, Clone)]
10pub struct MPReachNLRI {
11    /// The Address Family Identifier of the routes being announced.
12    pub afi: AFI,
13
14    /// The Subsequent Address Family Identifier of the routes being announced.
15    pub safi: SAFI,
16
17    /// The next hop of the announced routes.
18    pub next_hop: Vec<u8>,
19
20    /// The routes that are being announced.
21    pub announced_routes: Vec<NLRIEncoding>,
22}
23
24impl MPReachNLRI {
25    /// Parse MPUnreachNLRI information
26    pub(crate) fn parse(
27        stream: &mut impl Read,
28        length: u16,
29        capabilities: &Capabilities,
30    ) -> Result<MPReachNLRI, Error> {
31        let afi = AFI::try_from(stream.read_u16::<BigEndian>()?)?;
32        let safi = SAFI::try_from(stream.read_u8()?)?;
33
34        let next_hop_length = stream.read_u8()?;
35        let mut next_hop = vec![0; usize::from(next_hop_length)];
36        stream.read_exact(&mut next_hop)?;
37
38        let _reserved = stream.read_u8()?;
39
40        // ----------------------------
41        // Read NLRI
42        // ----------------------------
43        let size = length - u16::from(5 + next_hop_length);
44
45        let mut buffer = vec![0; usize::from(size)];
46        stream.read_exact(&mut buffer)?;
47        let mut cursor = Cursor::new(buffer);
48        let mut announced_routes: Vec<NLRIEncoding> = Vec::with_capacity(4);
49
50        match afi {
51            AFI::IPV4 | AFI::IPV6 => {
52                while cursor.position() < u64::from(size) {
53                    match safi {
54                        // Labelled nexthop
55                        // TODO Add label parsing and support capabilities.MULTIPLE_LABELS
56                        SAFI::Mpls => {
57                            let path_id = if util::detect_add_path_prefix(&mut cursor, 255)? {
58                                Some(cursor.read_u32::<BigEndian>()?)
59                            } else {
60                                None
61                            };
62                            let len_bits = cursor.read_u8()?;
63                            // Protect against malformed messages
64                            if len_bits == 0 {
65                                return Err(Error::new(
66                                    ErrorKind::Other,
67                                    "Invalid prefix length 0",
68                                ));
69                            }
70
71                            let len_bytes = (f32::from(len_bits) / 8.0).ceil() as u8;
72                            // discard label, resv and s-bit for now
73                            cursor.read_exact(&mut [0u8; 3])?;
74                            let remaining = (len_bytes - 3) as usize;
75
76                            let mut pfx_buf = afi.empty_buffer();
77                            cursor.read_exact(&mut pfx_buf[..remaining])?;
78
79                            // len_bits - MPLS info
80                            let pfx_len = len_bits - 24;
81                            let prefix = Prefix::new(afi, pfx_len, pfx_buf);
82
83                            match path_id {
84                                Some(path_id) => announced_routes.push(
85                                    NLRIEncoding::IP_MPLS_WITH_PATH_ID((prefix, 0u32, path_id)),
86                                ),
87                                None => {
88                                    announced_routes.push(NLRIEncoding::IP_MPLS((prefix, 0u32)))
89                                }
90                            };
91                        }
92                        SAFI::MplsVpn => {
93                            let len_bits = cursor.read_u8()?;
94                            let len_bytes = (f32::from(len_bits) / 8.0).ceil() as u8;
95                            // discard label, resv and s-bit for now
96                            cursor.read_exact(&mut [0u8; 3])?;
97                            let remaining = (len_bytes - 3) as usize;
98
99                            let rd = cursor.read_u64::<BigEndian>()?;
100                            let mut pfx_buf = afi.empty_buffer();
101                            cursor.read_exact(&mut pfx_buf[..(remaining - 8)])?;
102
103                            // len_bits - MPLS info - Route Distinguisher
104                            let pfx_len = len_bits - 24 - 64;
105                            let prefix = Prefix::new(afi, pfx_len, pfx_buf);
106
107                            announced_routes.push(NLRIEncoding::IP_VPN_MPLS((rd, prefix, 0u32)));
108                        }
109                        #[cfg(feature = "flowspec")]
110                        SAFI::Flowspec => {
111                            let mut nlri_length = cursor.read_u8()?;
112                            let mut filters: Vec<FlowspecFilter> = vec![];
113                            while nlri_length > 0 {
114                                let cur_position = cursor.position();
115                                filters.push(FlowspecFilter::parse(&mut cursor, afi)?);
116                                nlri_length -= (cursor.position() - cur_position) as u8;
117                            }
118                            announced_routes.push(NLRIEncoding::FLOWSPEC(filters));
119                        }
120                        #[cfg(feature = "flowspec")]
121                        SAFI::FlowspecVPN => {
122                            unimplemented!();
123                        }
124                        _ => {
125                            if capabilities.EXTENDED_PATH_NLRI_SUPPORT {
126                                while cursor.position() < u64::from(size) {
127                                    let path_id = cursor.read_u32::<BigEndian>()?;
128                                    let prefix = Prefix::parse(&mut cursor, afi)?;
129                                    announced_routes
130                                        .push(NLRIEncoding::IP_WITH_PATH_ID((prefix, path_id)));
131                                }
132                            } else {
133                                while cursor.position() < u64::from(size) {
134                                    let prefix = Prefix::parse(&mut cursor, afi)?;
135                                    announced_routes.push(NLRIEncoding::IP(prefix));
136                                }
137                            }
138                        }
139                    };
140                }
141            }
142            AFI::L2VPN => {
143                let _len = cursor.read_u16::<BigEndian>()?;
144                let rd = cursor.read_u64::<BigEndian>()?;
145                let ve_id = cursor.read_u16::<BigEndian>()?;
146                let label_block_offset = cursor.read_u16::<BigEndian>()?;
147                let label_block_size = cursor.read_u16::<BigEndian>()?;
148                let label_base = cursor.read_u24::<BigEndian>()?;
149
150                announced_routes.push(NLRIEncoding::L2VPN((
151                    rd,
152                    ve_id,
153                    label_block_offset,
154                    label_block_size,
155                    label_base,
156                )));
157            }
158            AFI::BGPLS => unimplemented!(),
159        };
160
161        Ok(MPReachNLRI {
162            afi,
163            safi,
164            next_hop,
165            announced_routes,
166        })
167    }
168
169    /// Encode Multiprotocol Reach NLRI to bytes
170    pub fn encode(&self, mut buf: &mut impl Write) -> Result<(), Error> {
171        buf.write_u16::<BigEndian>(self.afi as u16)?;
172        buf.write_u8(self.safi as u8)?;
173        buf.write_u8(self.next_hop.len() as u8)?;
174        buf.write_all(&self.next_hop)?;
175        buf.write_u8(0u8)?; // Reserved
176        for nlri in &self.announced_routes {
177            nlri.encode(&mut buf)?;
178        }
179        Ok(())
180    }
181}
182
183/// Used when withdrawing routes to non-IPv4 addresses.
184#[derive(Debug, Clone)]
185pub struct MPUnreachNLRI {
186    /// The Address Family Identifier of the routes being withdrawn.
187    pub afi: AFI,
188
189    /// The Subsequent Address Family Identifier of the routes being withdrawn.
190    pub safi: SAFI,
191
192    /// The routes being withdrawn.
193    pub withdrawn_routes: Vec<NLRIEncoding>,
194}
195
196impl MPUnreachNLRI {
197    /// Parse MPUnreachNLRI information
198    pub(crate) fn parse(
199        stream: &mut impl Read,
200        length: u16,
201        capabilities: &Capabilities,
202    ) -> Result<MPUnreachNLRI, Error> {
203        let afi = AFI::try_from(stream.read_u16::<BigEndian>()?)?;
204        let safi = SAFI::try_from(stream.read_u8()?)?;
205
206        // ----------------------------
207        // Read NLRI
208        // ----------------------------
209        let size = length - 3;
210
211        let mut buffer = vec![0; usize::from(size)];
212        stream.read_exact(&mut buffer)?;
213        let mut cursor = Cursor::new(buffer);
214        let mut withdrawn_routes: Vec<NLRIEncoding> = Vec::with_capacity(4);
215
216        while cursor.position() < u64::from(size) {
217            match safi {
218                // Labelled nexthop
219                // TODO Add label parsing and support capabilities.MULTIPLE_LABELS
220                SAFI::Mpls => {
221                    let path_id = if util::detect_add_path_prefix(&mut cursor, 255)? {
222                        Some(cursor.read_u32::<BigEndian>()?)
223                    } else {
224                        None
225                    };
226                    let len_bits = cursor.read_u8()?;
227                    // Protect against malformed messages
228                    if len_bits == 0 {
229                        return Err(Error::new(ErrorKind::Other, "Invalid prefix length 0"));
230                    }
231
232                    let len_bytes = (f32::from(len_bits) / 8.0).ceil() as u8;
233                    // discard label, resv and s-bit for now
234                    cursor.read_exact(&mut [0u8; 3])?;
235                    let remaining = (len_bytes - 3) as usize;
236
237                    let mut pfx_buf = afi.empty_buffer();
238                    cursor.read_exact(&mut pfx_buf[..remaining])?;
239
240                    // len_bits - MPLS info
241                    let pfx_len = len_bits - 24;
242                    match path_id {
243                        Some(path_id) => withdrawn_routes.push(NLRIEncoding::IP_MPLS_WITH_PATH_ID(
244                            (Prefix::new(afi, pfx_len, pfx_buf), 0, path_id),
245                        )),
246                        None => withdrawn_routes.push(NLRIEncoding::IP_MPLS((
247                            Prefix::new(afi, pfx_len, pfx_buf),
248                            0,
249                        ))),
250                    };
251                }
252                SAFI::MplsVpn => {
253                    let len_bits = cursor.read_u8()?;
254                    let len_bytes = (f32::from(len_bits) / 8.0).ceil() as u8;
255
256                    // Upon reception, the value of the Compatibility field MUST be ignored.
257                    cursor.read_exact(&mut [0u8; 3])?;
258
259                    let remaining = (len_bytes - 3) as usize;
260
261                    let rd = cursor.read_u64::<BigEndian>()?;
262                    let mut pfx_buf = afi.empty_buffer();
263                    cursor.read_exact(&mut pfx_buf[..(remaining - 8)])?;
264
265                    // len_bits - MPLS info - Route Distinguisher
266                    let pfx_len = len_bits - 24 - 64;
267                    withdrawn_routes.push(NLRIEncoding::IP_VPN_MPLS((
268                        rd,
269                        Prefix::new(afi, pfx_len, pfx_buf),
270                        0u32,
271                    )));
272                }
273                #[cfg(feature = "flowspec")]
274                SAFI::Flowspec => {
275                    let mut nlri_length = cursor.read_u8()?;
276                    let mut filters: Vec<FlowspecFilter> = vec![];
277                    while nlri_length > 0 {
278                        let cur_position = cursor.position();
279                        filters.push(FlowspecFilter::parse(&mut cursor, afi)?);
280                        nlri_length -= (cursor.position() - cur_position) as u8;
281                    }
282                    withdrawn_routes.push(NLRIEncoding::FLOWSPEC(filters));
283                }
284                #[cfg(feature = "flowspec")]
285                SAFI::FlowspecVPN => {
286                    unimplemented!();
287                }
288                // DEFAULT
289                _ => {
290                    if capabilities.EXTENDED_PATH_NLRI_SUPPORT {
291                        while cursor.position() < u64::from(size) {
292                            let path_id = cursor.read_u32::<BigEndian>()?;
293                            let prefix = Prefix::parse(&mut cursor, afi)?;
294                            withdrawn_routes.push(NLRIEncoding::IP_WITH_PATH_ID((prefix, path_id)));
295                        }
296                    } else {
297                        while cursor.position() < u64::from(size) {
298                            let prefix = Prefix::parse(&mut cursor, afi)?;
299                            withdrawn_routes.push(NLRIEncoding::IP(prefix));
300                        }
301                    }
302                }
303            };
304        }
305
306        Ok(MPUnreachNLRI {
307            afi,
308            safi,
309            withdrawn_routes,
310        })
311    }
312
313    /// Encode Multiprotocol Reach NLRI to bytes
314    pub fn encode(&self, buf: &mut impl Write) -> Result<(), Error> {
315        buf.write_u16::<BigEndian>(self.afi as u16)?;
316        buf.write_u8(self.safi as u8)?;
317        for nlri in &self.withdrawn_routes {
318            nlri.encode(buf)?;
319        }
320        Ok(())
321    }
322}