use crate::models::*;
use crate::parser::bgp::attributes::attr_03_next_hop::parse_mp_next_hop;
use crate::parser::bgp::attributes::attr_29_linkstate::parse_link_state_nlri;
use crate::parser::{parse_nlri_list, ReadUtils};
use crate::ParserError;
use bytes::{BufMut, Bytes, BytesMut};
use log::warn;
pub fn parse_nlri(
mut input: Bytes,
afi: &Option<Afi>,
safi: &Option<Safi>,
prefixes: &Option<&[NetworkPrefix]>,
reachable: bool, additional_paths: bool, ) -> Result<AttributeValue, ParserError> {
let first_byte_zero = input.first().map(|b| *b == 0).unwrap_or(false);
let afi = match afi {
Some(afi) => {
if first_byte_zero {
input.read_afi()?
} else {
afi.to_owned()
}
}
None => input.read_afi()?,
};
let safi = match safi {
Some(safi) => {
if first_byte_zero {
input.read_safi()?
} else {
safi.to_owned()
}
}
None => input.read_safi()?,
};
let mut next_hop = None;
if reachable {
let next_hop_length = input.read_u8()? as usize;
input.has_n_remaining(next_hop_length)?;
let next_hop_bytes = input.split_to(next_hop_length);
next_hop = parse_mp_next_hop(next_hop_bytes)?;
}
let (prefixes, labeled_prefixes, link_state_nlris) =
if afi == Afi::LinkState && (safi == Safi::LinkState || safi == Safi::LinkStateVpn) {
if reachable {
if input.read_u8()? != 0 {
warn!("NLRI reserved byte not 0 (parsing NLRI Link-State)");
}
}
let ls_nlri = parse_link_state_nlri(input, afi, safi, next_hop, reachable)?;
let link_state_list = ls_nlri.link_state_nlris;
(Vec::new(), None, link_state_list)
} else if safi == Safi::MplsLabel {
let config = LabeledNlriConfig {
add_path: additional_paths,
mode: LabeledNlriMode::MultiLabel, max_labels: 16,
peer_max_labels: None,
};
if reachable {
if input.read_u8()? != 0 {
warn!("NLRI reserved byte not 0 (parsing MPLS-labeled NLRI)");
}
let labeled = parse_labeled_nlri(&mut input, afi, &config)?;
(Vec::new(), Some(labeled), None)
} else {
let prefixes = parse_labeled_withdrawal_nlri(&mut input, afi, &config)?;
(prefixes, None, None)
}
} else {
let prefixes = match prefixes {
Some(pfxs) => {
if first_byte_zero {
if reachable {
if input.read_u8()? != 0 {
warn!("NLRI reserved byte not 0 (parsing NLRI prefixes)");
}
}
parse_nlri_list(input, additional_paths, &afi)?
} else {
pfxs.to_vec()
}
}
None => {
if reachable {
if input.read_u8()? != 0 {
warn!("NLRI reserved byte not 0 (parsing NLRI prefixes)");
}
}
parse_nlri_list(input, additional_paths, &afi)?
}
};
(prefixes, None, None)
};
let nlri = Nlri {
afi,
safi,
next_hop,
prefixes,
labeled_prefixes,
link_state_nlris,
flowspec_nlris: None,
};
match reachable {
true => Ok(AttributeValue::MpReachNlri(nlri)),
false => Ok(AttributeValue::MpUnreachNlri(nlri)),
}
}
pub fn encode_nlri(nlri: &Nlri, reachable: bool, add_path: bool) -> Result<Bytes, ParserError> {
let mut bytes = BytesMut::new();
bytes.put_u16(nlri.afi as u16);
bytes.put_u8(nlri.safi as u8);
if let Some(next_hop) = &nlri.next_hop {
if !reachable {
warn!("NLRI next hop should not be set for unreachable NLRI (encoding NLRI)");
}
let next_hop_bytes = match next_hop {
NextHopAddress::Ipv4(ip) => ip.octets().to_vec(),
NextHopAddress::Ipv6(ip) => ip.octets().to_vec(),
NextHopAddress::Ipv6LinkLocal(ip1, ip2) => {
let mut ip_bytes = ip1.octets().to_vec();
ip_bytes.extend_from_slice(&ip2.octets());
ip_bytes
}
NextHopAddress::VpnIpv6(rd, ip) => {
let mut ip_bytes = rd.0.to_vec(); ip_bytes.extend_from_slice(&ip.octets()); ip_bytes
}
NextHopAddress::VpnIpv6LinkLocal(rd1, ip1, rd2, ip2) => {
let mut ip_bytes = rd1.0.to_vec(); ip_bytes.extend_from_slice(&ip1.octets()); ip_bytes.extend_from_slice(&rd2.0); ip_bytes.extend_from_slice(&ip2.octets()); ip_bytes
}
};
bytes.put_u8(next_hop_bytes.len() as u8);
bytes.put_slice(&next_hop_bytes);
}
if nlri.safi == Safi::MplsLabel {
if reachable {
bytes.put_u8(0);
}
if let Some(labeled_prefixes) = &nlri.labeled_prefixes {
for labeled in labeled_prefixes {
let mode = if labeled.labels.len() <= 1 {
crate::models::LabeledNlriMode::SingleLabel
} else {
crate::models::LabeledNlriMode::MultiLabel
};
let encoded = crate::models::encode_labeled_prefix(
labeled, mode, add_path, None, )
.map_err(|e| {
ParserError::ParseError(format!("Failed to encode labeled prefix: {:?}", e))
})?;
bytes.extend_from_slice(&encoded);
}
}
} else if nlri.afi == Afi::LinkState {
if reachable {
bytes.put_u8(0);
}
if let Some(link_state_nlris) = &nlri.link_state_nlris {
for ls_nlri in link_state_nlris {
bytes.put_u16(ls_nlri.nlri_type as u16);
bytes.put_u16(0); }
}
} else {
if reachable {
bytes.put_u8(0);
}
for prefix in &nlri.prefixes {
bytes.extend(prefix.encode());
}
}
Ok(bytes.freeze())
}
#[cfg(test)]
mod tests {
use super::*;
use ipnet::IpNet;
use std::net::Ipv4Addr;
use std::str::FromStr;
#[test]
fn test_parsing_nlri_simple() {
let test_bytes = Bytes::from(vec![
0x00, 0x01, 0x01, 0x04, 0xC0, 0x00, 0x02, 0x01, 0x00, 0x18, 0xC0, 0x00, 0x02, ]);
let res = parse_nlri(test_bytes, &None, &None, &None, true, false);
if let Ok(AttributeValue::MpReachNlri(nlri)) = res {
assert_eq!(nlri.afi, Afi::Ipv4);
assert_eq!(nlri.safi, Safi::Unicast);
assert_eq!(
nlri.next_hop,
Some(NextHopAddress::Ipv4(
Ipv4Addr::from_str("192.0.2.1").unwrap()
))
);
assert_eq!(
nlri.prefixes,
vec![NetworkPrefix::from_str("192.0.2.0/24").unwrap()]
);
}
}
#[test]
fn test_parsing_nlri_passed_in_afi() {
let test_bytes = Bytes::from(vec![
0x00, 0x01, 0x01, 0x04, 0xC0, 0x00, 0x02, 0x01, 0x00, 0x18, 0xC0, 0x00, 0x02, ]);
let res = parse_nlri(
test_bytes,
&Some(Afi::Ipv4),
&Some(Safi::Unicast),
&None,
true,
false,
);
if let Ok(AttributeValue::MpReachNlri(nlri)) = res {
assert_eq!(nlri.afi, Afi::Ipv4);
assert_eq!(nlri.safi, Safi::Unicast);
assert_eq!(
nlri.next_hop,
Some(NextHopAddress::Ipv4(
Ipv4Addr::from_str("192.0.2.1").unwrap()
))
);
assert_eq!(
nlri.prefixes,
vec![NetworkPrefix::from_str("192.0.2.0/24").unwrap()]
);
}
}
#[test]
fn test_parsing_nlri_errors() {
let test_bytes = Bytes::from(vec![
0x00, 0x01, 0x01, 0x04, 0xC0, 0x00, 0x02, 0x00, 0x18, 0xC0, 0x00, 0x02, ]);
let res = parse_nlri(
test_bytes,
&Some(Afi::Ipv4),
&Some(Safi::Unicast),
&None,
true,
false,
);
assert!(res.is_err());
}
#[test]
fn test_parsing_nlri_add_path() {
let test_bytes = Bytes::from(vec![
0x00, 0x01, 0x01, 0x04, 0xC0, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x7B, 0x18, 0xC0, 0x00, 0x02, ]);
let res = parse_nlri(test_bytes, &None, &None, &None, true, true);
if let Ok(AttributeValue::MpReachNlri(nlri)) = res {
assert_eq!(nlri.afi, Afi::Ipv4);
assert_eq!(nlri.safi, Safi::Unicast);
assert_eq!(
nlri.next_hop,
Some(NextHopAddress::Ipv4(
Ipv4Addr::from_str("192.0.2.1").unwrap()
))
);
let prefix = NetworkPrefix::new(IpNet::from_str("192.0.2.0/24").unwrap(), Some(123));
assert_eq!(nlri.prefixes[0], prefix);
assert_eq!(nlri.prefixes[0].path_id, prefix.path_id);
} else {
panic!("Unexpected result: {res:?}");
}
}
#[test]
fn test_encode_nlri() {
let nlri = Nlri {
afi: Afi::Ipv4,
safi: Safi::Unicast,
next_hop: Some(NextHopAddress::Ipv4(
Ipv4Addr::from_str("10.0.0.1").unwrap(),
)),
prefixes: vec![NetworkPrefix {
prefix: IpNet::from_str("192.0.1.0/24").unwrap(),
path_id: None,
}],
labeled_prefixes: None,
link_state_nlris: None,
flowspec_nlris: None,
};
let bytes = encode_nlri(&nlri, true, false).unwrap();
assert_eq!(
bytes,
Bytes::from(vec![
0x00, 0x01, 0x01, 0x04, 0x0A, 0x00, 0x00, 0x01, 0x00, 0x18, 0xC0, 0x00, 0x01, ])
);
let parsed_nlri = parse_nlri(bytes, &None, &None, &None, true, false).unwrap();
assert_eq!(parsed_nlri, AttributeValue::MpReachNlri(nlri));
let nlri = Nlri {
afi: Afi::Ipv4,
safi: Safi::Unicast,
next_hop: Some(NextHopAddress::Ipv4(
Ipv4Addr::from_str("10.0.0.1").unwrap(),
)),
prefixes: vec![NetworkPrefix {
prefix: IpNet::from_str("192.0.1.0/24").unwrap(),
path_id: Some(123),
}],
labeled_prefixes: None,
link_state_nlris: None,
flowspec_nlris: None,
};
let bytes = encode_nlri(&nlri, true, true).unwrap();
assert_eq!(
bytes,
Bytes::from(vec![
0x00, 0x01, 0x01, 0x04, 0x0A, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x7B, 0x18, 0xC0, 0x00, 0x01, ])
);
}
#[test]
fn test_parsing_unreachable_nlri() {
let test_bytes = Bytes::from(vec![
0x00, 0x01, 0x01, 0x18, 0xC0, 0x00, 0x02, ]);
let res = parse_nlri(test_bytes, &None, &None, &None, false, false);
if let Ok(AttributeValue::MpUnreachNlri(nlri)) = res {
assert_eq!(nlri.afi, Afi::Ipv4);
assert_eq!(nlri.safi, Safi::Unicast);
assert_eq!(nlri.next_hop, None);
assert_eq!(
nlri.prefixes,
vec![NetworkPrefix::from_str("192.0.2.0/24").unwrap()]
);
} else {
panic!("Unexpected result: {res:?}");
}
}
#[test]
fn test_encode_unreachable_nlri() {
let nlri = Nlri {
afi: Afi::Ipv4,
safi: Safi::Unicast,
next_hop: None,
prefixes: vec![NetworkPrefix {
prefix: IpNet::from_str("192.0.1.0/24").unwrap(),
path_id: None,
}],
labeled_prefixes: None,
link_state_nlris: None,
flowspec_nlris: None,
};
let bytes = encode_nlri(&nlri, false, false).unwrap();
assert_eq!(
bytes,
Bytes::from(vec![
0x00, 0x01, 0x01, 0x18, 0xC0, 0x00, 0x01, ])
);
let parsed_nlri = parse_nlri(bytes, &None, &None, &None, false, false).unwrap();
assert_eq!(parsed_nlri, AttributeValue::MpUnreachNlri(nlri));
let nlri_with_next_hop = Nlri {
afi: Afi::Ipv4,
safi: Safi::Unicast,
next_hop: Some(NextHopAddress::Ipv4(
Ipv4Addr::from_str("10.0.0.1").unwrap(),
)),
prefixes: vec![NetworkPrefix {
prefix: IpNet::from_str("192.0.1.0/24").unwrap(),
path_id: None,
}],
labeled_prefixes: None,
link_state_nlris: None,
flowspec_nlris: None,
};
let bytes = encode_nlri(&nlri_with_next_hop, false, false).unwrap();
assert_eq!(
bytes,
Bytes::from(vec![
0x00, 0x01, 0x01, 0x04, 0x0A, 0x00, 0x00, 0x01, 0x18, 0xC0, 0x00, 0x01, ])
);
}
}