Skip to content

Commit

Permalink
bgp, ldp, ospfv3: optimize decoding of IP prefixes
Browse files Browse the repository at this point in the history
This update makes some simple tweaks to avoid allocating memory when
decoding IP prefixes from a byte stream.

During testing with 1 million BGP routes, the daemon used to spend
about 2.6% of its time decoding prefixes from BGP messages, according
to flamegraphs. Now it's down to just 0.2%. Big win!

Signed-off-by: Renato Westphal <[email protected]>
  • Loading branch information
rwestphal committed Feb 27, 2024
1 parent 0a92a02 commit cac45c9
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 45 deletions.
12 changes: 6 additions & 6 deletions holo-bgp/src/packet/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -994,9 +994,9 @@ pub(crate) fn decode_ipv4_prefix(
}

// Parse prefix address (variable length).
let mut prefix_bytes = vec![0; plen_wire];
buf.copy_to_slice(&mut prefix_bytes);
let prefix = Ipv4Addr::from_slice(&prefix_bytes);
let mut prefix_bytes = [0; Ipv4Addr::LENGTH];
buf.copy_to_slice(&mut prefix_bytes[..plen_wire]);
let prefix = Ipv4Addr::from(prefix_bytes);
let prefix = Ipv4Network::new(prefix, plen)
.map(|prefix| prefix.apply_mask())
.map_err(|_| UpdateMessageError::InvalidNetworkField)?;
Expand All @@ -1023,9 +1023,9 @@ pub(crate) fn decode_ipv6_prefix(
}

// Parse prefix address (variable length).
let mut prefix_bytes = vec![0; plen_wire];
buf.copy_to_slice(&mut prefix_bytes);
let prefix = Ipv6Addr::from_slice(&prefix_bytes);
let mut prefix_bytes = [0; Ipv6Addr::LENGTH];
buf.copy_to_slice(&mut prefix_bytes[..plen_wire]);
let prefix = Ipv6Addr::from(prefix_bytes);
let prefix = Ipv6Network::new(prefix, plen)
.map(|prefix| prefix.apply_mask())
.map_err(|_| UpdateMessageError::InvalidNetworkField)?;
Expand Down
20 changes: 15 additions & 5 deletions holo-ldp/src/packet/messages/label.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
// SPDX-License-Identifier: MIT
//

use std::net::IpAddr;
use std::net::{Ipv4Addr, Ipv6Addr};

use bytes::{Buf, BufMut, Bytes, BytesMut};
use holo_utils::ip::{
AddressFamily, IpAddrExt, IpNetworkExt, Ipv4NetworkExt, Ipv6NetworkExt,
AddressFamily, IpAddrExt, IpNetworkExt, Ipv4AddrExt, Ipv4NetworkExt,
Ipv6AddrExt, Ipv6NetworkExt,
};
use holo_utils::mpls::Label;
use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network};
Expand Down Expand Up @@ -478,10 +479,19 @@ impl FecElem {
}

// Parse prefix.
let mut prefix_bytes = vec![0; plen_wire];
buf.copy_to_slice(&mut prefix_bytes);
let prefix = match af {
AddressFamily::Ipv4 => {
let mut prefix_bytes = [0; Ipv4Addr::LENGTH];
buf.copy_to_slice(&mut prefix_bytes[..plen_wire]);
Ipv4Addr::from(prefix_bytes).into()
}
AddressFamily::Ipv6 => {
let mut prefix_bytes = [0; Ipv6Addr::LENGTH];
buf.copy_to_slice(&mut prefix_bytes[..plen_wire]);
Ipv6Addr::from(prefix_bytes).into()
}
};
*tlv_rlen -= plen_wire as u16;
let prefix = IpAddr::from_slice(af, &prefix_bytes);
IpNetwork::new(prefix, plen)
.map(|prefix| FecElem::Prefix(prefix.apply_mask()))
.map_err(|_| DecodeError::InvalidTlvValue(tlvi.clone()))
Expand Down
17 changes: 13 additions & 4 deletions holo-ospf/src/ospfv3/packet/lsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use bytes::{Buf, BufMut, Bytes, BytesMut};
use derive_new::new;
use enum_as_inner::EnumAsInner;
use holo_utils::bytes::{BytesExt, BytesMutExt};
use holo_utils::ip::{AddressFamily, IpAddrExt};
use holo_utils::ip::{AddressFamily, IpAddrExt, Ipv4AddrExt, Ipv6AddrExt};
use holo_utils::mpls::Label;
use holo_utils::sr::{IgpAlgoType, Sid};
use ipnetwork::IpNetwork;
Expand Down Expand Up @@ -2891,9 +2891,18 @@ fn decode_prefix(
buf: &mut Bytes,
) -> DecodeResult<IpNetwork> {
let plen_wire = prefix_wire_len(plen);
let mut prefix_bytes = vec![0; plen_wire];
buf.copy_to_slice(&mut prefix_bytes);
let prefix = IpAddr::from_slice(af, &prefix_bytes);
let prefix = match af {
AddressFamily::Ipv4 => {
let mut prefix_bytes = [0; Ipv4Addr::LENGTH];
buf.copy_to_slice(&mut prefix_bytes[..plen_wire]);
Ipv4Addr::from(prefix_bytes).into()
}
AddressFamily::Ipv6 => {
let mut prefix_bytes = [0; Ipv6Addr::LENGTH];
buf.copy_to_slice(&mut prefix_bytes[..plen_wire]);
Ipv6Addr::from(prefix_bytes).into()
}
};
IpNetwork::new(prefix, plen).map_err(|_| DecodeError::InvalidIpPrefix)
}

Expand Down
30 changes: 0 additions & 30 deletions holo-utils/src/ip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ pub trait IpAddrExt {
// Returns vector of bytes that make up this address.
fn bytes(&self) -> Vec<u8>;

// Creates an IpAddr from a slice of bytes.
fn from_slice(af: AddressFamily, bytes: &[u8]) -> IpAddr;

// Returns true if this is an usable address.
fn is_usable(&self) -> bool;

Expand All @@ -48,9 +45,6 @@ pub trait IpAddrExt {
pub trait Ipv4AddrExt {
const LENGTH: usize;

// Creates an Ipv4Addr from a slice of bytes.
fn from_slice(bytes: &[u8]) -> Ipv4Addr;

// Returns true if this is an usable address.
fn is_usable(&self) -> bool;
}
Expand All @@ -59,9 +53,6 @@ pub trait Ipv4AddrExt {
pub trait Ipv6AddrExt {
const LENGTH: usize;

// Creates an Ip6Addr from a slice of bytes.
fn from_slice(bytes: &[u8]) -> Ipv6Addr;

// Returns true if this is an usable address.
fn is_usable(&self) -> bool;
}
Expand Down Expand Up @@ -242,13 +233,6 @@ impl IpAddrExt for IpAddr {
}
}

fn from_slice(af: AddressFamily, bytes: &[u8]) -> IpAddr {
match af {
AddressFamily::Ipv4 => Ipv4Addr::from_slice(bytes).into(),
AddressFamily::Ipv6 => Ipv6Addr::from_slice(bytes).into(),
}
}

fn is_usable(&self) -> bool {
!(self.is_loopback() || self.is_multicast() || self.is_unspecified())
}
Expand Down Expand Up @@ -283,13 +267,6 @@ impl IpAddrKind for IpAddr {
impl Ipv4AddrExt for Ipv4Addr {
const LENGTH: usize = 4;

fn from_slice(bytes: &[u8]) -> Ipv4Addr {
let mut bytes = bytes.to_vec();
bytes.resize(4, 0);
let bytes: [u8; 4] = bytes[..].try_into().unwrap();
Ipv4Addr::from(bytes)
}

fn is_usable(&self) -> bool {
!(self.is_loopback()
|| self.is_broadcast()
Expand Down Expand Up @@ -320,13 +297,6 @@ impl IpAddrKind for Ipv4Addr {
impl Ipv6AddrExt for Ipv6Addr {
const LENGTH: usize = 16;

fn from_slice(bytes: &[u8]) -> Ipv6Addr {
let mut bytes = bytes.to_vec();
bytes.resize(16, 0);
let bytes: [u8; 16] = bytes[..].try_into().unwrap();
Ipv6Addr::from(bytes)
}

fn is_usable(&self) -> bool {
!(self.is_loopback() || self.is_multicast() || self.is_unspecified())
}
Expand Down

0 comments on commit cac45c9

Please sign in to comment.