-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Christian
committed
Feb 2, 2019
1 parent
0c78991
commit cfef946
Showing
10 changed files
with
780 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,3 +8,6 @@ Cargo.lock | |
|
||
# These are backup files generated by rustfmt | ||
**/*.rs.bk | ||
|
||
# IDE specific ignores | ||
/.idea/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
language: rust | ||
cache: cargo | ||
sudo: required | ||
|
||
rust: | ||
- stable | ||
- beta | ||
|
||
before_script: | ||
- rustup component add clippy | ||
|
||
addons: | ||
apt: | ||
packages: | ||
- libcurl4-openssl-dev | ||
- libelf-dev | ||
- libdw-dev | ||
- cmake | ||
- gcc | ||
- binutils-dev | ||
- libiberty-dev | ||
|
||
script: | ||
- cargo build --verbose | ||
- cargo test --verbose | ||
- cargo doc --verbose | ||
- cargo clippy --verbose | ||
|
||
after_success: | ||
- wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz && tar xzf master.tar.gz && cd kcov-master && mkdir build && cd build && cmake .. && make && make install DESTDIR=../../kcov-build && cd ../.. && rm -rf kcov-master | ||
- for file in target/debug/*[^\.d]; do mkdir -p "target/cov/$(basename $file)"; ./kcov-build/usr/local/bin/kcov --exclude-pattern=/.cargo,/usr/lib --verify "target/cov/$(basename $file)" "$file"; done; | ||
- bash <(curl -s https://codecov.io/bash) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
[package] | ||
name = "bgp-rs" | ||
version = "0.1.0" | ||
authors = ["Christian Veenman <[email protected]>"] | ||
edition = '2018' | ||
readme = "README.md" | ||
keywords = ["bgp", "parser"] | ||
categories = ["parsing", "network-programming"] | ||
repository = "https://github.com/DevQps/bgp-rs" | ||
homepage = "https://github.com/DevQps/bgp-rs" | ||
documentation = "https://docs.rs/bgp-rs" | ||
description = "A library for parsing Border Gateway Protocol (BGP) formatted streams." | ||
license = "GPL-3.0" | ||
exclude = [ | ||
"README.md", | ||
"res/*", | ||
"tests/*", | ||
".travis.yml" | ||
] | ||
|
||
[badges] | ||
travis-ci = { repository = "DevQps/bgp-rs", branch = "master" } | ||
codecov = { repository = "DevQps/bgp-rs", branch = "master", service = "github" } | ||
maintenance = { status = "actively-developed" } | ||
|
||
[dependencies.byteorder] | ||
version = "1.3.1" | ||
features = ["i128"] | ||
|
||
[dev-dependencies] | ||
libflate = "0.1" | ||
mrt-rs = "0.3" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,8 @@ | ||
# bgp-rs | ||
A parser for the Border Gateway Protocol (BGP) in Rust | ||
# Border Gateway Protocol in Rust (bgp-rs) | ||
[![Build Status](https://travis-ci.com/DevQps/bgp-rs.svg?branch=master)](https://travis-ci.com/DevQps/bgp-rs) [![codecov](https://codecov.io/gh/DevQps/bgp-rs/branch/master/graph/badge.svg)](https://codecov.io/gh/DevQps/bgp-rs) | ||
|
||
A library for parsing Border Gateway Protocol (BGP) formatted streams in Rust. | ||
Messages such as UPDATE, OPEN, KEEPALIVE and | ||
|
||
## Examples & Documentation | ||
For examples and documentation look [here](https://docs.rs/bgp-rs/). |
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,272 @@ | ||
use crate::{Prefix, AFI}; | ||
use byteorder::{BigEndian, ReadBytesExt}; | ||
use std::io::{Cursor, Error, ErrorKind, Read}; | ||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; | ||
|
||
#[derive(Debug)] | ||
#[allow(non_camel_case_types)] | ||
pub enum Attribute { | ||
ORIGIN(Origin), | ||
AS_PATH(ASPath), | ||
NEXT_HOP(IpAddr), | ||
MULTI_EXIT_DISC(u32), | ||
LOCAL_PREF(u32), | ||
ATOMIC_AGGREGATOR, | ||
AGGREGATOR((u32, Ipv4Addr)), | ||
COMMUNITY(Vec<u32>), | ||
ORIGINATOR_ID(u32), | ||
CLUSTER_LIST, | ||
DPA, | ||
ADVERTISER, | ||
CLUSTER_ID, | ||
MP_REACH_NLRI(MPReachNLRI), | ||
MP_UNREACH_NLRI(MPUnreachNLRI), | ||
EXTENDED_COMMUNITIES(Vec<u64>), | ||
AS4_PATH, | ||
AS4_AGGREGATOR, | ||
SSA, | ||
CONNECTOR, | ||
AS_PATHLIMIT, | ||
PMSI_TUNNEL, | ||
TUNNEL_ENCAPSULATION, | ||
TRAFFIC_ENGINEERING, | ||
IPV6_SPECIFIC_EXTENDED_COMMUNITY, | ||
AIGP, | ||
PE_DISTINGUISHER_LABELS, | ||
BGP_LS, | ||
LARGE_COMMUNITY(Vec<(u32, u32, u32)>), | ||
BGPSEC_PATH, | ||
BGP_PREFIX_SID, | ||
ATTR_SET, | ||
} | ||
|
||
impl Attribute { | ||
pub fn parse(stream: &mut Read) -> Result<Attribute, Error> { | ||
let flags = stream.read_u8()?; | ||
let code = stream.read_u8()?; | ||
|
||
// Check if the Extended Length bit is set. | ||
let length: u16 = if flags & (1 << 4) == 0 { | ||
stream.read_u8()? as u16 | ||
} else { | ||
stream.read_u16::<BigEndian>()? | ||
}; | ||
|
||
match code { | ||
1 => Ok(Attribute::ORIGIN(Origin::parse(stream)?)), | ||
2 => Ok(Attribute::AS_PATH(ASPath::parse(stream, length)?)), | ||
3 => { | ||
let ip: IpAddr = if length == 4 { | ||
IpAddr::V4(Ipv4Addr::from(stream.read_u32::<BigEndian>()?)) | ||
} else { | ||
IpAddr::V6(Ipv6Addr::from(stream.read_u128::<BigEndian>()?)) | ||
}; | ||
|
||
Ok(Attribute::NEXT_HOP(ip)) | ||
} | ||
4 => Ok(Attribute::MULTI_EXIT_DISC(stream.read_u32::<BigEndian>()?)), | ||
5 => Ok(Attribute::LOCAL_PREF(stream.read_u32::<BigEndian>()?)), | ||
6 => Ok(Attribute::ATOMIC_AGGREGATOR), | ||
7 => { | ||
let asn = if length == 6 { | ||
stream.read_u16::<BigEndian>()? as u32 | ||
} else { | ||
stream.read_u32::<BigEndian>()? | ||
}; | ||
|
||
let ip = Ipv4Addr::from(stream.read_u32::<BigEndian>()?); | ||
Ok(Attribute::AGGREGATOR((asn, ip))) | ||
} | ||
8 => { | ||
let mut communities = Vec::with_capacity((length / 4) as usize); | ||
for _ in 0..(length / 4) { | ||
communities.push(stream.read_u32::<BigEndian>()?) | ||
} | ||
|
||
Ok(Attribute::COMMUNITY(communities)) | ||
} | ||
9 => Ok(Attribute::ORIGINATOR_ID(stream.read_u32::<BigEndian>()?)), | ||
14 => Ok(Attribute::MP_REACH_NLRI(MPReachNLRI::parse( | ||
stream, length, | ||
)?)), | ||
15 => Ok(Attribute::MP_UNREACH_NLRI(MPUnreachNLRI::parse( | ||
stream, length, | ||
)?)), | ||
16 => { | ||
let mut communities = Vec::with_capacity((length / 8) as usize); | ||
for _ in 0..(length / 8) { | ||
communities.push(stream.read_u64::<BigEndian>()?) | ||
} | ||
|
||
Ok(Attribute::EXTENDED_COMMUNITIES(communities)) | ||
} | ||
32 => { | ||
let mut communities: Vec<(u32, u32, u32)> = | ||
Vec::with_capacity((length / 12) as usize); | ||
for _ in 0..(length / 12) { | ||
let admin = stream.read_u32::<BigEndian>()?; | ||
let part1 = stream.read_u32::<BigEndian>()?; | ||
let part2 = stream.read_u32::<BigEndian>()?; | ||
communities.push((admin, part1, part2)) | ||
} | ||
|
||
Ok(Attribute::LARGE_COMMUNITY(communities)) | ||
} | ||
x => { | ||
let mut buffer = vec![0; length as usize]; | ||
stream.read_exact(&mut buffer); | ||
|
||
Err(Error::new( | ||
ErrorKind::Other, | ||
format!("Unknown path attribute type found: {}", x), | ||
)) | ||
} | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub enum Origin { | ||
IGP, | ||
EGP, | ||
INCOMPLETE, | ||
} | ||
|
||
impl Origin { | ||
pub fn parse(stream: &mut Read) -> Result<Origin, Error> { | ||
match stream.read_u8()? { | ||
0 => Ok(Origin::IGP), | ||
1 => Ok(Origin::EGP), | ||
2 => Ok(Origin::INCOMPLETE), | ||
_ => Err(Error::new(ErrorKind::Other, "Unknown origin type found.")), | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct ASPath { | ||
pub segments: Vec<Segment>, | ||
} | ||
|
||
impl ASPath { | ||
// TODO: Give argument that determines the AS size. | ||
pub fn parse(stream: &mut Read, length: u16) -> Result<ASPath, Error> { | ||
// Create an AS_PATH struct with a capacity of 1, since AS_SETs | ||
// or multiple AS_SEQUENCES, are not seen often anymore. | ||
let mut path = ASPath { | ||
segments: Vec::with_capacity(1), | ||
}; | ||
|
||
// While there are multiple AS_PATH segments, parse the segments. | ||
let mut size = length; | ||
while size != 0 { | ||
let segment_type = stream.read_u8()?; | ||
let length = stream.read_u8()?; | ||
let mut values: Vec<u32> = Vec::with_capacity(length as usize); | ||
|
||
for _ in 0..length { | ||
values.push(stream.read_u32::<BigEndian>()?); | ||
} | ||
|
||
match segment_type { | ||
1 => path.segments.push(Segment::AS_SEQUENCE(values)), | ||
2 => path.segments.push(Segment::AS_SET(values)), | ||
x => { | ||
return Err(Error::new( | ||
ErrorKind::Other, | ||
format!("Unknown AS_PATH segment type found: {}", x), | ||
)); | ||
} | ||
} | ||
|
||
size -= 2 + (length as u16 * 4); | ||
} | ||
|
||
Ok(path) | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
#[allow(non_camel_case_types)] | ||
pub enum Segment { | ||
AS_SEQUENCE(Vec<u32>), | ||
AS_SET(Vec<u32>), | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct MPReachNLRI { | ||
pub afi: AFI, | ||
pub safi: u8, | ||
pub next_hop: Vec<u8>, | ||
pub announced_routes: Vec<Prefix>, | ||
} | ||
|
||
impl MPReachNLRI { | ||
// TODO: Give argument that determines the AS size. | ||
pub fn parse(stream: &mut Read, length: u16) -> Result<MPReachNLRI, Error> { | ||
let afi = AFI::from(stream.read_u16::<BigEndian>()?)?; | ||
let safi = stream.read_u8()?; | ||
|
||
let next_hop_length = stream.read_u8()?; | ||
let mut next_hop = vec![0; next_hop_length as usize]; | ||
stream.read_exact(&mut next_hop)?; | ||
|
||
let _reserved = stream.read_u8()?; | ||
|
||
// ---------------------------- | ||
// Read NLRI | ||
// ---------------------------- | ||
let size = length - (5 + next_hop_length) as u16; | ||
|
||
let mut buffer = vec![0; size as usize]; | ||
stream.read_exact(&mut buffer); | ||
let mut cursor = Cursor::new(buffer); | ||
let mut announced_routes: Vec<Prefix> = Vec::with_capacity(4); | ||
|
||
while cursor.position() < size as u64 { | ||
announced_routes.push(Prefix::parse(&mut cursor)?); | ||
} | ||
|
||
Ok(MPReachNLRI { | ||
afi, | ||
safi, | ||
next_hop, | ||
announced_routes, | ||
}) | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct MPUnreachNLRI { | ||
pub afi: AFI, | ||
pub safi: u8, | ||
pub withdrawn_routes: Vec<Prefix>, | ||
} | ||
|
||
impl MPUnreachNLRI { | ||
// TODO: Give argument that determines the AS size. | ||
pub fn parse(stream: &mut Read, length: u16) -> Result<MPUnreachNLRI, Error> { | ||
let afi = AFI::from(stream.read_u16::<BigEndian>()?)?; | ||
let safi = stream.read_u8()?; | ||
|
||
// ---------------------------- | ||
// Read NLRI | ||
// ---------------------------- | ||
let size = length - 3 as u16; | ||
|
||
let mut buffer = vec![0; size as usize]; | ||
stream.read_exact(&mut buffer); | ||
let mut cursor = Cursor::new(buffer); | ||
let mut withdrawn_routes: Vec<Prefix> = Vec::with_capacity(4); | ||
|
||
while cursor.position() < size as u64 { | ||
withdrawn_routes.push(Prefix::parse(&mut cursor)?); | ||
} | ||
|
||
Ok(MPUnreachNLRI { | ||
afi, | ||
safi, | ||
withdrawn_routes, | ||
}) | ||
} | ||
} |
Oops, something went wrong.