Start of major rewrite/cleanup.
Some checks are pending
Rust / build (push) Waiting to run

This commit is contained in:
Rayhaan Jaufeerally
2025-04-19 18:19:37 +02:00
12 changed files with 1913 additions and 124 deletions

18
crates/packet/Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[package]
name = "bgp-packet"
description.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
version.workspace = true
[dependencies]
bitfield.workspace = true
bytes.workspace = true
eyre.workspace = true
nom.workspace = true
serde.workspace = true
serde_repr.workspace = true
thiserror.workspace = true

View File

@ -0,0 +1,70 @@
use std::fmt::Display;
use eyre::bail;
use serde_repr::{Deserialize_repr, Serialize_repr};
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum AddressFamilyId {
Ipv4 = 1,
Ipv6 = 2,
}
impl TryFrom<u16> for AddressFamilyId {
type Error = eyre::Error;
fn try_from(value: u16) -> Result<Self, Self::Error> {
Ok(match value {
x if x == Self::Ipv4 as u16 => Self::Ipv4,
x if x == Self::Ipv6 as u16 => Self::Ipv6,
other => bail!("Unknown AddressFamily: {}", other),
})
}
}
impl Display for AddressFamilyId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AddressFamilyId::Ipv4 => write!(f, "Ipv4"),
AddressFamilyId::Ipv6 => write!(f, "Ipv6"),
}
}
}
/// Represents a Subsequent Address Family Identifier.
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum SubsequentAfi {
Unicast = 1,
Multicast = 2,
NlriWithMpls = 4,
MplsLabelledVpn = 128,
MulticastMplsVpn = 129,
}
impl TryFrom<u8> for SubsequentAfi {
type Error = eyre::Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Ok(match value {
x if x == Self::Unicast as u8 => Self::Unicast,
x if x == Self::Multicast as u8 => Self::Multicast,
x if x == Self::NlriWithMpls as u8 => Self::NlriWithMpls,
x if x == Self::MplsLabelledVpn as u8 => Self::MplsLabelledVpn,
x if x == Self::MulticastMplsVpn as u8 => Self::MulticastMplsVpn,
other => bail!("Unknown SubsequentAfi: {}", other),
})
}
}
impl Display for SubsequentAfi {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SubsequentAfi::Unicast => write!(f, "Unicast"),
SubsequentAfi::Multicast => write!(f, "Multicast"),
SubsequentAfi::NlriWithMpls => write!(f, "NlriWithMpls"),
SubsequentAfi::MplsLabelledVpn => write!(f, "MplsLabelledVpn"),
SubsequentAfi::MulticastMplsVpn => write!(f, "MulticastMplsVpn"),
}
}
}

149
crates/packet/src/errors.rs Normal file
View File

@ -0,0 +1,149 @@
use thiserror::Error;
#[derive(Debug, Error)]
#[repr(u8)]
pub enum BgpError {
#[error("Message header error")]
MsgHeader(MsgHeaderSubcode) = 1,
#[error("Open message error")]
OpenMsg(OpenMsgSubcode) = 2,
#[error("Update message error")]
UpdateMsg(UpdateMsgSubcode) = 3,
#[error("Hold timer expired")]
HoldTimer = 4,
#[error("Finite state machine error")]
FsmError(FsmSubcode) = 5,
#[error("Cease")]
Cease(CeaseSubcode) = 6,
#[error("Route refresh message error")]
RouteRefresh(RouteRefreshSubcode) = 7,
#[error("Send hold timer expired")]
SendHoldTimer = 8,
}
#[derive(Debug, Error)]
pub enum MsgHeaderSubcode {
#[error("Connection not synchronized")]
ConnNotSynchronized = 1,
#[error("Bad message length")]
BadMessageLength = 2,
#[error("Bad message type")]
BadMessagType = 3,
}
#[derive(Debug, Error)]
pub enum OpenMsgSubcode {
#[error("Unsupported BGP version number")]
UnsupportedVersion = 1,
#[error("Bad peer AS number")]
BadPeerAs = 2,
#[error("Bad BGP identifier")]
BadBgpId = 3,
#[error("Unsupported optional parameter")]
UnsupportedOptionalParam = 4,
#[error("Unacceptable hold time")]
UnacceptableHoldTime = 6,
#[error("Unsupported capability")]
UnsupportedCapability = 7,
#[error("Role mismatch")]
RoleMismatch = 8,
}
#[derive(Debug, Error)]
pub enum UpdateMsgSubcode {
#[error("Malformed attribute list")]
MalforedAttrs = 1,
#[error("Unrecognized well known attribute")]
UnrecognizedWellKnownAttr = 2,
#[error("Missing well known attribute")]
MissingWellKnown = 3,
#[error("Attribute flags error")]
AttributeFlags = 4,
#[error("Attribute length error")]
AttributeLength = 5,
#[error("Invalid origin")]
InvalidOrigin = 6,
#[error("Invalid next hop")]
InvalidNextHop = 8,
#[error("Optional attribute error")]
OptionalAttribute = 9,
#[error("Invalid network field")]
InvalidNetworkField = 10,
#[error("Malformed AS path")]
MalformedAsPath = 11,
}
#[derive(Debug, Error)]
pub enum FsmSubcode {
#[error("Received an unexpected message in OpenSent state")]
UnexpectedOpenSent = 1,
#[error("Received an unexpected message in OpenConfirm state")]
UnexpectedOpenConfirm = 2,
#[error("Received an unexpected message in Established state")]
UnexpectedEstablished = 3,
}
#[derive(Debug, Error)]
pub enum CeaseSubcode {
#[error("Maximum number of prefixes reached")]
MaxPrefixes = 1,
#[error("Administrative shutdown")]
AdminShutdown = 2,
#[error("Peer deconfigured")]
PeerDeconf = 3,
#[error("Administrative reset")]
AdminReset = 4,
#[error("Connection rejected")]
ConnRejected = 5,
#[error("Configuration change")]
ConfChange = 6,
#[error("Connection collision resolution")]
ConnCollisionResolution = 7,
#[error("Out of resources")]
OutOfResources = 8,
#[error("Hard reset")]
HardReset = 9,
#[error("BFD down")]
BfdDown = 10,
}
#[derive(Debug, Error)]
pub enum RouteRefreshSubcode {
#[error("Invalid message length")]
InvalidLength = 1,
}

View File

@ -0,0 +1,215 @@
use std::fmt::Display;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
use bytes::{Buf, BufMut, BytesMut};
use eyre::{Result, bail};
use serde::{Deserialize, Serialize};
use crate::constants::AddressFamilyId;
use crate::parser::{ParserContext, ToWireError};
/// IpPrefix represents some IP address prefix, for a specific AddressFamilyId.
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
pub struct IpPrefix {
pub address_family: AddressFamilyId,
pub prefix: Vec<u8>,
pub length: u8,
}
impl IpPrefix {
pub fn new(address_family: AddressFamilyId, prefix: Vec<u8>, length: u8) -> Result<Self> {
// Ensure that the prefix we are given contains the right number of bytes corresponding to the prefix length.
if prefix.len() < ((length + 7) / 8).into() {
bail!(
"Mismatched prefix {:?} for given prefix length: {}",
prefix,
length
);
}
Ok(Self {
address_family,
prefix,
length,
})
}
pub fn to_wire(&self, _: &ParserContext, out: &mut BytesMut) -> Result<(), ToWireError> {
// Verify that there is enough space to write the IpPrefix.
if out.remaining() < (self.prefix.len() + 1) {
Err(ToWireError::OutBufferOverflow)?;
}
// Write length and prefix.
out.put_u8(self.length);
out.put(self.prefix.as_slice());
Ok(())
}
}
impl TryFrom<&str> for IpPrefix {
type Error = eyre::Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let parts: Vec<&str> = value.split("/").collect();
if parts.len() != 2 {
bail!(
"Expected IpPrefix in format prefix/length but got: {}",
value
);
}
let length: u8 = u8::from_str_radix(parts[1], 10).map_err(eyre::Error::from)?;
let mut octets;
let afi: AddressFamilyId;
if parts[0].contains(":") {
afi = AddressFamilyId::Ipv6;
let addr: Ipv6Addr = Ipv6Addr::from_str(parts[0]).map_err(eyre::Error::from)?;
octets = addr.octets().to_vec();
} else if parts[0].contains(".") {
afi = AddressFamilyId::Ipv4;
let addr: Ipv4Addr = Ipv4Addr::from_str(parts[0]).map_err(eyre::Error::from)?;
octets = addr.octets().to_vec();
} else {
bail!("Could not figure out address type")
}
// Truncate the octets we have to the right number of bytes to match the prefix length..
if length % 8 == 0 {
// We can cleanly truncate the number of bytes since we are at a byte boundary.
octets.truncate((length / 8).into());
} else {
// We need to keep length % 8 bits of the last byte.
let num_bytes = (length / 8) + 1;
let mask = u8::MAX << (8 - (length % 8));
octets.truncate(num_bytes.into());
// Fix up the last byte.
let last_pos = octets.len() - 1;
octets[last_pos] &= mask;
}
Ok(IpPrefix {
address_family: afi,
prefix: octets,
length,
})
}
}
impl Display for IpPrefix {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.address_family {
AddressFamilyId::Ipv4 => {
// Pad the prefix with 0 bytes if it's less than 4 bytes long.
let bytes = &mut self.prefix.clone();
if bytes.len() < 4 {
bytes.extend(std::iter::repeat(0).take(4 - bytes.len()));
}
let four_bytes: [u8; 4] = bytes
.as_slice()
.try_into()
.map_err(|_| std::fmt::Error {})?;
let ipv4_addr = Ipv4Addr::from(four_bytes);
write!(f, "{}/{}", ipv4_addr, self.length)
}
AddressFamilyId::Ipv6 => {
let bytes = &mut self.prefix.clone();
if bytes.len() < 16 {
bytes.extend(std::iter::repeat(0).take(16 - bytes.len()));
}
let sixteen_bytes: [u8; 16] = bytes
.as_slice()
.try_into()
.map_err(|_| std::fmt::Error {})?;
let ipv6_addr = Ipv6Addr::from(sixteen_bytes);
write!(f, "{}/{}", ipv6_addr, self.length)
}
}
}
}
#[cfg(test)]
mod tests {
use crate::constants::AddressFamilyId;
use super::IpPrefix;
macro_rules! verify_roundtrip {
($name:ident, $prefix_str:expr, $afi:expr, $bytes:expr, $length:expr) => {
#[test]
fn $name() {
let ip_prefix = IpPrefix::try_from($prefix_str).unwrap();
assert_eq!(ip_prefix.address_family, $afi);
assert_eq!(ip_prefix.prefix, $bytes);
assert_eq!(ip_prefix.length, $length);
let to_str: &str = &ip_prefix.to_string();
assert_eq!(IpPrefix::try_from(to_str).unwrap(), ip_prefix);
}
};
}
verify_roundtrip!(
verify_roundtrip_ipv4_24,
"10.1.2.0/24",
AddressFamilyId::Ipv4,
vec![10, 1, 2],
24
);
// Verify truncation.
verify_roundtrip!(
verify_roundtrip_ipv4_19,
"10.245.123.0/19",
AddressFamilyId::Ipv4,
vec![10, 245, 96],
19
);
// Verify truncation.
verify_roundtrip!(
verify_roundtrip_ipv4_3,
"192.168.1.0/3",
AddressFamilyId::Ipv4,
vec![192],
3
);
// Verify default address.
verify_roundtrip!(
verify_roundtrip_ipv4_0,
"0.0.0.0/0",
AddressFamilyId::Ipv4,
vec![],
0
);
verify_roundtrip!(
verify_roundtrip_ipv6_48,
"2001:db8:cafe::/48",
AddressFamilyId::Ipv6,
vec![32, 1, 13, 184, 202, 254],
48
);
// Verify truncation.
verify_roundtrip!(
verify_roundtrip_ipv6_32,
"2001:db8:cafe::/32",
AddressFamilyId::Ipv6,
vec![32, 1, 13, 184],
32
);
verify_roundtrip!(
verify_roundtrip_ipv6_0,
"::/0",
AddressFamilyId::Ipv6,
vec![],
0
);
}

5
crates/packet/src/lib.rs Normal file
View File

@ -0,0 +1,5 @@
pub mod constants;
pub mod errors;
pub mod ip_prefix;
pub mod message;
pub mod parser;

View File

@ -0,0 +1,905 @@
use std::net::{Ipv4Addr, Ipv6Addr};
use bitfield::bitfield;
use bytes::BufMut;
use bytes::BytesMut;
use eyre::Context;
use eyre::Result;
use eyre::bail;
use eyre::eyre;
use nom::Err::Failure;
use nom::IResult;
use nom::Parser;
use nom::number::complete::be_u8;
use nom::number::complete::be_u16;
use nom::number::complete::be_u32;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::constants::AddressFamilyId;
use crate::constants::SubsequentAfi;
use crate::ip_prefix::IpPrefix;
use crate::parser::BgpParserError;
use crate::parser::ParserContext;
/// A sentinel AS number used to denote that the actual AS number is provided as
/// a 4 byte value in the capabilities instead.
pub const AS_TRANS: u16 = 23456;
/// Version number used in the BGP4 protocol.
pub const BGP4_VERSION: u8 = 4;
/// Message represents the top-level messages in the BGP protocol.
#[derive(Debug, Serialize, Deserialize)]
pub enum Message {
Open(OpenMessage),
Update(UpdateMessage),
Notification(NotificationMessage),
KeepAlive,
// RouteRefresh(RouteRefreshMessage),
}
impl Message {}
#[derive(Debug, Serialize, Deserialize)]
pub enum MessageType {
/// BGP Open message (RFC 4271).
Open = 1,
/// BGP Update message (RFC 4271).
Update = 2,
/// BGP Notification message (RFC 4275).
Notification = 3,
/// BGP KeepAlive message (RFC 4271).
KeepAlive = 4,
/// BGP Route Refresh message (RFC 2918).
RouteRefresh = 5,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct OpenMessage {
/// Version of the BGP protocol in use.
pub version: u8,
/// AS Number of the BGP speaker, or AS_TRANS if using 4 byte ASN.
pub asn: u16,
/// Hold time parameter in seconds.
pub hold_time: u16,
/// Global identifier of the BGP speaker.
pub identifier: Ipv4Addr,
/// Options.
pub options: Vec<OpenOption>,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum OpenOption {
Capabilities(Vec<Capability>),
/// BGP extended open options length (RFC 9072).
ExtendedLength(u32),
}
#[derive(Debug, Serialize, Deserialize)]
pub enum Capability {
/// MultiProtocol Extension (RFC 2858).
MultiProtocol { afi: u16, safi: u8 },
/// Route Refresh capability (RFC 2918).
RouteRefresh {},
/// Outbound Route Filtering (RFC 5291).
/// https://datatracker.ietf.org/doc/html/rfc5291
OutboundRouteFilter {},
/// Extended Next Hop encoding (RFC 8950).
ExtendedNextHop {},
/// Extended Message (RFC 8654).
ExtendedMessage {},
/// BGPSec (RFC 8205).
BgpSec {},
/// Multiple labels compatibility (RFC 8277).
MultiLabelCompat {},
/// Graceful restart capability (RFC 4724).
GracefulRestart {},
/// Four Byte ASN (RFC 4274).
FourByteAsn { asn: u32 },
/// Additional Path (RFC 7911).
AddPath {},
/// Enhanced Route Refresh (RFC 7313).
EnhancedRouteRefresh {},
}
/// Represents a BGP Update message.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateMessage {}
bitfield! {
pub struct PathAttributeFlags(u8);
impl Debug;
u8;
optional, set_optional: 0;
transitive, set_transitive: 1;
partial, set_partial: 2;
extended_length, set_extended_length: 3;
}
#[repr(u8)]
pub enum PathAttribute {
Origin(OriginPathAttribute) = 1,
ASPath(AsPathAttribute) = 2,
NextHop(NextHopPathAttribute) = 3,
MultiExitDisc(MultiExitDiscPathAttribute) = 4,
LocalPref(LocalPrefPathAttribute) = 5,
AtomicAggregate(AtomicAggregatePathAttribute) = 6,
Aggregator(AggregatorPathAttribute) = 7,
Communitites(CommunitiesPathAttribute) = 8,
MpReachNlri(MpReachNlriPathAttribute) = 14,
MpUnreachNlri(MpUnreachNlriPathAttribute) = 15,
ExtendedCommunities(ExtendedCommunitiesPathAttribute) = 16,
LargeCommunities(LargeCommunitiesPathAttribute) = 32,
UnknownPathAttribute {
flags: PathAttributeFlags,
type_code: u8,
payload: Vec<u8>,
},
}
impl PathAttribute {
/// The from_wire parser for `PathAttribute` consumes type and length which it uses to
/// determine how many bytes to take and pass down to the corresponding sub-parser.
pub fn from_wire<'a>(
ctx: &ParserContext,
buf: &'a [u8],
) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
let (buf, attr_flags) = be_u8(buf).map(|(buf, b)| (buf, PathAttributeFlags(b)))?;
let (buf, type_code) = be_u8(buf)?;
let (buf, length): (_, u16) = if attr_flags.extended_length() {
be_u16(buf)?
} else {
be_u8(buf).map(|(buf, b)| (buf, b as u16))?
};
todo!();
}
}
/// Origin path attribute is a mandatory attribute defined in RFC4271.
#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum OriginPathAttribute {
IGP = 0,
EGP = 1,
INCOMPLETE = 2,
}
impl TryFrom<u8> for OriginPathAttribute {
type Error = eyre::Error;
fn try_from(value: u8) -> Result<Self> {
match value {
0 => Ok(Self::IGP),
1 => Ok(Self::EGP),
2 => Ok(Self::INCOMPLETE),
other => bail!("Unexpected origin code {}", other),
}
}
}
/// ASPathAttribute is a well-known mandatory attribute that contains a list of TLV encoded path
/// segments. Type is either 1 for AS_SET or 2 for AS_SEQUENCE, length is a 1 octet field
/// containing the number of ASNS and the value contains the ASNs. This is defined in Section 4.3
/// of RFC4271.
#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
pub struct AsPathAttribute {
pub segments: Vec<AsPathSegment>,
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
pub struct AsPathSegment {
/// ordered is true when representing an AS_SEQUENCE, andd false when
/// representing an AS_SET.
pub ordered: bool,
/// Path is the list of ASNs.
pub path: Vec<u32>,
}
impl AsPathAttribute {
pub fn from_asns(asns: Vec<u32>) -> PathAttribute {
let segment = AsPathSegment {
ordered: true,
path: asns,
};
PathAttribute::ASPath(AsPathAttribute {
segments: vec![segment],
})
}
pub fn from_wire<'a>(
ctx: &ParserContext,
buf: &'a [u8],
) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
let parse_segment = |ctx: &ParserContext,
buf: &'a [u8]|
-> IResult<&'a [u8], AsPathSegment, BgpParserError<&'a [u8]>> {
let (buf, typ) = be_u8(buf)?;
let (buf, len) = be_u8(buf)?;
let (buf, asns): (_, Vec<u32>) = match ctx.four_octet_asn {
Some(true) => {
nom::multi::many_m_n(len as usize, len as usize, be_u32).parse(buf)?
}
Some(false) => nom::multi::many_m_n(len as usize, len as usize, be_u16)
.parse(buf)
.map(|(buf, asns)| (buf, asns.iter().map(|asn| *asn as u32).collect()))?,
None => {
return Err(nom::Err::Failure(BgpParserError::CustomText(
"Context must set four_octet_asn before being used",
)));
}
};
Ok((
buf,
AsPathSegment {
ordered: typ == 2,
path: asns,
},
))
};
let (buf, segments) =
nom::multi::many0(|buf: &'a [u8]| parse_segment(ctx, buf)).parse(buf)?;
Ok((buf, Self { segments }))
}
pub fn to_wire(&self, ctx: &ParserContext, out: &mut BytesMut) -> Result<()> {
if ctx.four_octet_asn.is_none_or(|val| !val) {
bail!("AsPathAttribute can only be sent for four_octet_asn enabled peers");
}
for segment in &self.segments {
// Segment type.
out.put_u8(if segment.ordered { 2 } else { 1 });
// Segment AS length.
out.put_u16(
segment
.path
.len()
.try_into()
.wrap_err("AS Path length too long")?,
);
// AS numbers.
for asn in &segment.path {
out.put_u32(*asn);
}
}
Ok(())
}
pub fn wire_len(&self, ctx: &ParserContext) -> Result<u16> {
let mut counter = 0;
for segment in &self.segments {
counter += match ctx.four_octet_asn {
Some(true) => 2 + (4 * segment.path.len()),
Some(false) => 2 + (2 * segment.path.len()),
None => bail!("ParserContext needs four_octet_asn set"),
};
counter += 2 + (4 * segment.path.len());
}
Ok(counter as u16)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct NextHopPathAttribute(pub Ipv4Addr);
impl NextHopPathAttribute {
pub fn from_wire<'a>(
_: &ParserContext,
buf: &'a [u8],
) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
let (buf, ip_u32) = be_u32(buf)?;
Ok((buf, Self(Ipv4Addr::from(ip_u32))))
}
pub fn to_wire(&self, out: &mut BytesMut) -> Result<()> {
out.put_u32(self.0.into());
Ok(())
}
pub fn wire_len(&self, _: &ParserContext) -> Result<u32> {
Ok(4)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct MultiExitDiscPathAttribute(pub u32);
impl MultiExitDiscPathAttribute {
pub fn from_wire<'a>(
_: &ParserContext,
buf: &'a [u8],
) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
let (buf, val) = be_u32(buf)?;
Ok((buf, Self(val)))
}
pub fn to_wire(&self, _: &ParserContext, out: &mut BytesMut) -> Result<()> {
out.put_u32(self.0);
Ok(())
}
pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
Ok(4)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct LocalPrefPathAttribute(pub u32);
impl LocalPrefPathAttribute {
pub fn from_wire<'a>(
_: &ParserContext,
buf: &'a [u8],
) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
let (buf, val) = be_u32(buf)?;
Ok((buf, Self(val)))
}
pub fn to_wire(&self, _: &ParserContext, out: &mut BytesMut) -> Result<()> {
out.put_u32(self.0);
Ok(())
}
pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
Ok(4)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct AtomicAggregatePathAttribute {}
impl AtomicAggregatePathAttribute {
pub fn from_wire<'a>(
_: &ParserContext,
buf: &'a [u8],
) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
Ok((buf, Self {}))
}
pub fn to_wire(&self, _: &ParserContext, _: &mut BytesMut) -> Result<()> {
Ok(())
}
pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
Ok(0)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct AggregatorPathAttribute {
pub asn: u32,
pub ip: Ipv4Addr,
}
impl AggregatorPathAttribute {
pub fn from_wire<'a>(
ctx: &ParserContext,
buf: &'a [u8],
) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
if ctx.four_octet_asn.is_none_or(|val| !val) {
return Err(nom::Err::Failure(BgpParserError::CustomText(
"AggregatorPathAttribute can only be parsed for four_octet_asn enabled peers",
)));
}
let (buf, asn) = be_u32(buf)?;
let (buf, ip) = be_u32(buf)?;
Ok((
buf,
Self {
asn,
ip: Ipv4Addr::from(ip),
},
))
}
pub fn to_wire(&self, ctx: &ParserContext, out: &mut BytesMut) -> Result<()> {
if ctx.four_octet_asn.is_none_or(|val| !val) {
bail!("AggregatorPathAttribute can only be sent for four_octet_asn enabled peers");
}
out.put_u32(self.asn);
out.put_u32(self.ip.into());
Ok(())
}
pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
Ok(8)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct CommunitiesPathAttribute(Vec<(u16, u16)>);
impl CommunitiesPathAttribute {
pub fn from_wire<'a>(
_: &ParserContext,
buf: &'a [u8],
) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
let (buf, values) = nom::multi::many0(|i| (be_u16, be_u16).parse(i)).parse(buf)?;
Ok((buf, CommunitiesPathAttribute(values)))
}
pub fn to_wire(&self, _: &ParserContext, out: &mut BytesMut) -> Result<()> {
for value in &self.0 {
out.put_u16(value.0);
out.put_u16(value.1);
}
Ok(())
}
pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
Ok((self.0.len() * 4) as u16)
}
}
/// Extended Communities as defined in https://www.rfc-editor.org/rfc/rfc4360.html.
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct ExtendedCommunitiesPathAttribute {
pub extended_communities: Vec<ExtendedCommunity>,
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub enum ExtendedCommunity {
/// AS Specific Extended Community as specified in Section 3.1 of RFC4360.
AsSpecific {
typ: u8,
sub_typ: u8,
global_admin: u16,
local_admin: u32,
},
/// Ipv4 Address Specific Extended Community as specified in Section 3.2 of RFC4360.
Ipv4AddrSpecific {
typ: u8,
sub_typ: u8,
global_admin: u32,
local_admin: u16,
},
/// Opaque Extended Community as specified in Section 3.3 of RFC4360.
Opaque {
typ: u8,
sub_typ: u8,
value: [u8; 5],
},
/// Route Target Community as specified in Section 4 of RFC4360.
RouteTarget {
typ: u8,
sub_typ: u8,
global_admin: u32,
local_admin: u16,
},
/// Route Origin Community as specified in Section 5 of RFC4360.
RouteOrigin {
typ: u8,
sub_typ: u8,
global_admin: u16,
local_admin: u32,
},
}
impl ExtendedCommunity {
pub fn from_wire<'a>(
_: &ParserContext,
buf: &'a [u8],
) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
let (buf, typ) = be_u8(buf)?;
let (buf, sub_typ) = be_u8(buf)?;
let (buf, parsed) = match (typ, sub_typ) {
// Route Target Extended Community.
(0x00 | 0x01 | 0x02, 0x02) => {
let (buf, global_admin) = be_u32(buf)?;
let (buf, local_admin) = be_u16(buf)?;
(
buf,
Self::RouteTarget {
typ,
sub_typ,
global_admin,
local_admin,
},
)
}
// Route Origin Extended Community.
(0x00 | 0x01 | 0x02, 0x03) => {
let (buf, global_admin) = be_u16(buf)?;
let (buf, local_admin) = be_u32(buf)?;
(
buf,
Self::RouteOrigin {
typ,
sub_typ,
global_admin,
local_admin,
},
)
}
// AS specific Extended Community.
(0x00 | 0x40, _) => {
let (buf, global_admin) = be_u16(buf)?;
let (buf, local_admin) = be_u32(buf)?;
(
buf,
Self::AsSpecific {
typ,
sub_typ,
global_admin,
local_admin,
},
)
}
// IPv4 Address Specific Extended Community.
(0x01 | 0x41, _) => {
let (buf, global_admin) = be_u32(buf)?;
let (buf, local_admin) = be_u16(buf)?;
(
buf,
Self::Ipv4AddrSpecific {
typ,
sub_typ,
global_admin,
local_admin,
},
)
}
_ => {
let (buf, payload) = nom::bytes::take(5_usize).parse(buf)?;
let value: [u8; 5] = payload.try_into().map_err(|_| {
Failure(BgpParserError::CustomText(
"Expected exactly 5 bytes from the parser",
))
})?;
(
buf,
Self::Opaque {
typ,
sub_typ,
value,
},
)
}
};
return Ok((buf, parsed));
}
pub fn to_wire(&self, _: &ParserContext, out: &mut BytesMut) -> Result<()> {
match self {
ExtendedCommunity::AsSpecific {
typ,
sub_typ,
global_admin,
local_admin,
} => {
out.put_u8(*typ);
out.put_u8(*sub_typ);
out.put_u16(*global_admin);
out.put_u32(*local_admin);
}
ExtendedCommunity::Ipv4AddrSpecific {
typ,
sub_typ,
global_admin,
local_admin,
} => {
out.put_u8(*typ);
out.put_u8(*sub_typ);
out.put_u32(*global_admin);
out.put_u16(*local_admin);
}
ExtendedCommunity::Opaque {
typ,
sub_typ,
value,
} => {
out.put_u8(*typ);
out.put_u8(*sub_typ);
out.put(&value[..]);
}
ExtendedCommunity::RouteTarget {
typ,
sub_typ,
global_admin,
local_admin,
} => {
out.put_u8(*typ);
out.put_u8(*sub_typ);
out.put_u32(*global_admin);
out.put_u16(*local_admin);
}
ExtendedCommunity::RouteOrigin {
typ,
sub_typ,
global_admin,
local_admin,
} => {
out.put_u8(*typ);
out.put_u8(*sub_typ);
out.put_u16(*global_admin);
out.put_u32(*local_admin);
}
}
Ok(())
}
pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
Ok(8)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LargeCommunitiesPathAttribute {
pub communities: Vec<LargeCommunity>,
}
impl LargeCommunitiesPathAttribute {
pub fn from_wire<'a>(
ctx: &ParserContext,
buf: &'a [u8],
) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
let (buf, communities) =
nom::multi::many1(|buf| LargeCommunity::from_wire(ctx, buf)).parse(buf)?;
Ok((buf, Self { communities }))
}
pub fn to_wire(&self, ctx: &ParserContext, out: &mut BytesMut) -> Result<()> {
for community in &self.communities {
community.to_wire(ctx, out)?;
}
Ok(())
}
pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
Ok(12_u16 * self.communities.len() as u16)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LargeCommunity {
pub global_admin: u32,
pub data_1: u32,
pub data_2: u32,
}
impl LargeCommunity {
pub fn from_wire<'a>(
_: &ParserContext,
buf: &'a [u8],
) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
let (buf, global_admin) = be_u32(buf)?;
let (buf, data_1) = be_u32(buf)?;
let (buf, data_2) = be_u32(buf)?;
Ok((
buf,
Self {
global_admin,
data_1,
data_2,
},
))
}
pub fn to_wire(&self, _: &ParserContext, out: &mut BytesMut) -> Result<()> {
out.put_u32(self.global_admin);
out.put_u32(self.data_1);
out.put_u32(self.data_2);
Ok(())
}
pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
Ok(12)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum NlriNextHop {
/// Represents an IPv4 address nexthop.
Ipv4(Ipv4Addr),
/// Represents a global IPv6 nexthop.
Ipv6(Ipv6Addr),
/// Represents a IPv6 Link Local and global address pair nexthop.
Ipv6WithLl {
global: Ipv6Addr,
link_local: Ipv6Addr,
},
}
impl NlriNextHop {
pub fn to_wire(&self, _: &ParserContext, out: &mut BytesMut) -> Result<()> {
match self {
NlriNextHop::Ipv4(ipv4_addr) => out.put(&ipv4_addr.octets()[..]),
NlriNextHop::Ipv6(ipv6_addr) => out.put(&ipv6_addr.octets()[..]),
NlriNextHop::Ipv6WithLl { global, link_local } => {
out.put(&global.octets()[..]);
out.put(&link_local.octets()[..])
}
}
Ok(())
}
pub fn wire_len(&self) -> u8 {
match self {
NlriNextHop::Ipv4(_) => 4,
NlriNextHop::Ipv6(_) => 16,
NlriNextHop::Ipv6WithLl { .. } => 32,
}
}
}
// parse_prefix is a helper function that implements an NLRI parser for the given AFI.
fn parse_prefix<'a>(
afi: AddressFamilyId,
buf: &'a [u8],
) -> IResult<&'a [u8], IpPrefix, BgpParserError<&'a [u8]>> {
let (buf, prefix_len) = be_u8(buf)?;
let byte_len = (prefix_len + 7) / 8;
let (buf, prefix_bytes) = nom::bytes::take(byte_len as usize).parse(buf)?;
let prefix = IpPrefix::new(afi, prefix_bytes.to_vec(), byte_len)
.map_err(|e| Failure(BgpParserError::Eyre(e)))?;
Ok((buf, prefix))
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MpReachNlriPathAttribute {
pub afi: AddressFamilyId,
pub safi: SubsequentAfi,
/// Next hop address (either IPv4 or IPv6 for now).
pub next_hop: NlriNextHop,
pub prefixes: Vec<IpPrefix>,
}
impl MpReachNlriPathAttribute {
pub fn from_wire<'a>(
_: &ParserContext,
buf: &'a [u8],
) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
let (buf, raw_afi) = be_u16(buf)?;
let afi =
AddressFamilyId::try_from(raw_afi).map_err(|e| Failure(BgpParserError::Eyre(e)))?;
let (buf, raw_safi) = be_u8(buf)?;
let safi =
SubsequentAfi::try_from(raw_safi).map_err(|e| Failure(BgpParserError::Eyre(e)))?;
let (buf, nh_len) = be_u8(buf)?;
let (buf, next_hop, prefixes) = match afi {
AddressFamilyId::Ipv4 => {
// Read the length of the nexthop which should equal 4.
if nh_len != 4 {
return Err(Failure(BgpParserError::Eyre(eyre!(
"Got nexthop address length {} when expected 4 for IPv4 AFI",
nh_len
))));
}
// Read the nexthop address which should now be an IPv4 address.
let (buf, nh_bytes) = be_u32(buf)?;
let next_hop = NlriNextHop::Ipv4(Ipv4Addr::from(nh_bytes));
let (buf, prefixes) =
nom::multi::many0(|buf| parse_prefix(AddressFamilyId::Ipv4, buf)).parse(buf)?;
(buf, next_hop, prefixes)
}
AddressFamilyId::Ipv6 => {
// https://datatracker.ietf.org/doc/html/rfc2545 defines that the nexthop address may be 16 or 32 bytes long.
let (buf, nh_bytes) = nom::bytes::take(nh_len as usize).parse(buf)?;
let nexthop = match nh_bytes.len() {
16 => {
// unwrap should never fire since we have explicitly checked the length.
let slice: [u8; 16] = nh_bytes.try_into().unwrap();
NlriNextHop::Ipv6(Ipv6Addr::from(slice))
}
32 => {
// unwrap should never fire since we have explicitly checked the length.
let slice: [u8; 32] = nh_bytes.try_into().unwrap();
let link_local_bytes: [u8; 16] = slice[0..16].try_into().unwrap();
let link_local = Ipv6Addr::from(link_local_bytes);
let global_bytes: [u8; 16] = slice[16..32].try_into().unwrap();
let global = Ipv6Addr::from(global_bytes);
NlriNextHop::Ipv6WithLl { global, link_local }
}
_ => {
return Err(Failure(BgpParserError::Eyre(eyre!(
"Mismatched IPv6 nexthop length, got {}, want 16 or 32",
nh_bytes.len()
))));
}
};
let (buf, prefixes) =
nom::multi::many0(|buf| parse_prefix(AddressFamilyId::Ipv6, buf)).parse(buf)?;
(buf, nexthop, prefixes)
}
};
Ok((
buf,
Self {
afi,
safi,
next_hop,
prefixes,
},
))
}
pub fn to_wire(&self, ctx: &ParserContext, out: &mut BytesMut) -> Result<()> {
out.put_u16(self.afi as u16);
out.put_u8(self.safi as u8);
out.put_u8(self.next_hop.wire_len());
self.next_hop.to_wire(ctx, out)?;
for prefix in &self.prefixes {
out.put_u8(prefix.length);
out.put(&prefix.prefix[..]);
}
Ok(())
}
pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
Ok(4_u16
+ self.next_hop.wire_len() as u16
+ self
.prefixes
.iter()
.map(|p| 1 + p.prefix.len() as u16)
.sum::<u16>())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MpUnreachNlriPathAttribute {
pub afi: AddressFamilyId,
pub safi: SubsequentAfi,
pub prefixes: Vec<IpPrefix>,
}
impl MpUnreachNlriPathAttribute {
pub fn from_wire<'a>(
_: &ParserContext,
buf: &'a [u8],
) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
let (buf, raw_afi) = be_u16(buf)?;
let afi =
AddressFamilyId::try_from(raw_afi).map_err(|e| Failure(BgpParserError::Eyre(e)))?;
let (buf, raw_safi) = be_u8(buf)?;
let safi =
SubsequentAfi::try_from(raw_safi).map_err(|e| Failure(BgpParserError::Eyre(e)))?;
let (buf, prefixes) = nom::multi::many0(|buf| parse_prefix(afi, buf)).parse(buf)?;
Ok((
buf,
MpUnreachNlriPathAttribute {
afi,
safi,
prefixes,
},
))
}
pub fn to_wire(&self, _: &ParserContext, out: &mut BytesMut) -> Result<()> {
out.put_u16(self.afi as u16);
out.put_u8(self.safi as u8);
for prefix in &self.prefixes {
out.put_u8(prefix.length);
out.put(&prefix.prefix[..]);
}
Ok(())
}
pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
Ok(3 + self
.prefixes
.iter()
.map(|p| 1 + p.prefix.len() as u16)
.sum::<u16>())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NotificationMessage {}

View File

@ -0,0 +1,49 @@
use std::fmt::Display;
use nom::error::{ErrorKind, ParseError};
use crate::constants::AddressFamilyId;
#[derive(Debug, Default)]
pub struct ParserContext {
/// Whether thi parser is being run with a peer that is RFC6793 compliant.
pub four_octet_asn: Option<bool>,
/// Which address family should be parsed by default with this parser.
pub address_family: Option<AddressFamilyId>,
}
#[derive(Debug)]
pub enum ToWireError {
/// There was not enough space in the output buffer to serialize the data into.
OutBufferOverflow,
/// Another error.
Other(eyre::Error),
}
impl Display for ToWireError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ToWireError::OutBufferOverflow => write!(f, "OutBufferOverflow"),
ToWireError::Other(report) => report.fmt(f),
}
}
}
impl std::error::Error for ToWireError {}
// Custom error type for the parser.
#[derive(Debug)]
pub enum BgpParserError<I> {
CustomText(&'static str),
Eyre(eyre::ErrReport),
Nom(I, ErrorKind),
}
impl<I> ParseError<I> for BgpParserError<I> {
fn from_error_kind(input: I, kind: ErrorKind) -> Self {
BgpParserError::Nom(input, kind)
}
fn append(_: I, _: ErrorKind, other: Self) -> Self {
other
}
}