Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
maxmouchet committed Aug 18, 2022
0 parents commit e104712
Show file tree
Hide file tree
Showing 32 changed files with 2,239 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[bumpversion]
current_version = 0.2.0
commit = True
tag = True
message = release(project): {current_version} → {new_version}

[bumpversion:file:Cargo.toml]
search = version = "{current_version}"
replace = version = "{new_version}"
37 changes: 37 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Build

on: [push, pull_request]

jobs:
test:
runs-on: ubuntu-latest
env:
# https://github.com/mozilla/grcov#example-how-to-generate-source-based-coverage-for-a-rust-project
LLVM_PROFILE_FILE: "%p-%m.profraw"
RUSTC_BOOTSTRAP: "1"
RUSTFLAGS: "-Zinstrument-coverage"
steps:
- uses: actions/checkout@v2
- name: Use Rust nightly
run: rustup default nightly
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
- name: Compute coverage
run: |
rustup component add llvm-tools-preview
curl -L https://github.com/mozilla/grcov/releases/latest/download/grcov-x86_64-unknown-linux-gnu.tar.bz2 | tar jxf -
./grcov . --binary-path ./target/debug/ -s . -t lcov --branch --ignore-not-existing --ignore "/*" -o lcov.info
- uses: codecov/codecov-action@v2
publish:
runs-on: ubuntu-latest
needs: [test]
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
steps:
- uses: actions/checkout@v2
- name: Use Rust nightly
run: rustup default nightly
- run: cargo publish
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Generated by Cargo
# will have compiled files and executables
/target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk
17 changes: 17 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
repos:
- repo: https://github.com/doublify/pre-commit-rust
rev: eeee35a
hooks:
- id: fmt
- id: cargo-check
- id: clippy
args: ["--", "-D", "warnings", "-A", "clippy::too-many-arguments"]

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0
hooks:
- id: trailing-whitespace
exclude: "README.md"
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
17 changes: 17 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "pantrace"
version = "0.2.0"
description = "Convert between traceroute formats."
license = "MIT"
authors = ["Maxime Mouchet <[email protected]>"]
repository = "https://github.com/dioptra-io/pantrace/"
edition = "2021"

[dependencies]
chrono = { version = "0.4", features = ["serde"] }
clap = { version = "3.1.12", features = ["derive"] }
deku = "0.12.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sha2 = "0.10.2"
warts = "0.3.2"
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2022 Sorbonne Université

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# pantrace

[![Build](https://img.shields.io/github/workflow/status/dioptra-io/pantrace/Build)](https://github.com/dioptra-io/pantrace/actions/workflows/build.yml)
[![Coverage](https://img.shields.io/codecov/c/github/dioptra-io/pantrace)](https://app.codecov.io/gh/dioptra-io/pantrace)
[![crates.io](https://img.shields.io/crates/v/pantrace?logo=crates)](https://crates.io/crates/pantrace/)
[![docs.rs](https://img.shields.io/docsrs/pantrace)](https://docs.rs/pantrace/)

Convert between traceroute formats.

## Installation

```bash
cargo install pantrace
```

## Usage

```bash
cat iris.jsonl | pantrace -f iris -t atlas > atlas.json
```
4 changes: 4 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
coverage:
status:
project: off
patch: off
14 changes: 14 additions & 0 deletions data/atlas_29792007.jsonl

Large diffs are not rendered by default.

1,000 changes: 1,000 additions & 0 deletions data/iris.jsonl

Large diffs are not rendered by default.

106 changes: 106 additions & 0 deletions src/atlas/from.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use crate::atlas::models::{
AtlasIcmpExt, AtlasIcmpExtMplsData, AtlasIcmpExtObj, AtlasTraceroute, AtlasTracerouteHop,
AtlasTracerouteReply,
};
use crate::internal::models::{MplsEntry, TracerouteReply};
use crate::utils::protocol_string;
use std::net::IpAddr;

impl AtlasTraceroute {
pub fn from_internal(replies: &[TracerouteReply]) -> Self {
// TODO: assert same-flow assumption?
let ref_reply = &replies[0];
let start_timestamp = replies
.iter()
.map(|reply| reply.capture_timestamp)
.min()
.unwrap();
let end_timestamp = replies
.iter()
.map(|reply| reply.capture_timestamp)
.max()
.unwrap();
AtlasTraceroute {
af: ref_reply.af(),
dst_addr: Some(IpAddr::from(ref_reply.probe_dst_addr)),
dst_name: ref_reply.probe_dst_addr.to_string(),
endtime: end_timestamp,
from: Some(IpAddr::from(ref_reply.probe_src_addr)),
msm_id: 0, // TODO
msm_name: "TODO".to_string(),
paris_id: ref_reply.probe_src_port,
prb_id: 0, // TODO
proto: protocol_string(ref_reply.probe_protocol),
result: replies
.group_by(|a, b| a.probe_ttl == b.probe_ttl)
.map(AtlasTracerouteHop::from_internal)
.collect(),
size: 0, // TODO
src_addr: Some(IpAddr::from(ref_reply.probe_src_addr)),
timestamp: start_timestamp,
kind: "traceroute".to_string(),
}
}
}

impl AtlasTracerouteHop {
pub fn from_internal(replies: &[TracerouteReply]) -> Self {
// TODO: assert same-hop assumption?
let ref_reply = &replies[0];
AtlasTracerouteHop {
hop: ref_reply.probe_ttl,
error: None,
result: replies
.iter()
.map(AtlasTracerouteReply::from_internal)
.collect(),
}
}
}

impl AtlasTracerouteReply {
pub fn from_internal(reply: &TracerouteReply) -> Self {
AtlasTracerouteReply {
from: Some(IpAddr::from(reply.reply_src_addr)),
rtt: (reply.rtt as f64) / 10.0,
size: reply.reply_size,
ttl: reply.reply_ttl,
icmpext: vec![AtlasIcmpExt::from_internal(&reply.reply_mpls_labels)],
}
}
}

impl AtlasIcmpExt {
pub fn from_internal(entries: &[MplsEntry]) -> Self {
// TODO: Store RFC4844 information.
AtlasIcmpExt {
version: 1,
rfc4884: 1,
obj: vec![AtlasIcmpExtObj::from_internal(entries)],
}
}
}

impl AtlasIcmpExtObj {
pub fn from_internal(entries: &[MplsEntry]) -> Self {
AtlasIcmpExtObj {
class: 1,
kind: 1,
mpls: entries
.iter()
.map(AtlasIcmpExtMplsData::from_internal)
.collect(),
}
}
}

impl AtlasIcmpExtMplsData {
pub fn from_internal(entry: &MplsEntry) -> Self {
AtlasIcmpExtMplsData {
label: entry.label,
exp: entry.exp,
s: entry.bottom_of_stack,
ttl: entry.ttl,
}
}
}
4 changes: 4 additions & 0 deletions src/atlas/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod from;
pub mod models;
pub mod reader;
pub mod to;
75 changes: 75 additions & 0 deletions src/atlas/models.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use crate::utils::{default_ipaddr, empty_string_as_none};
use chrono::serde::ts_seconds;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::net::IpAddr;

#[derive(Debug, Serialize, Deserialize)]
pub struct AtlasTraceroute {
pub af: u8,
pub dst_addr: Option<IpAddr>,
pub dst_name: String,
#[serde(with = "ts_seconds")]
pub endtime: DateTime<Utc>,
#[serde(default = "default_ipaddr", deserialize_with = "empty_string_as_none")]
pub from: Option<IpAddr>,
pub msm_id: u64,
pub msm_name: String,
#[serde(default)]
pub paris_id: u16,
pub prb_id: u64,
pub proto: String,
pub result: Vec<AtlasTracerouteHop>,
pub size: u16,
#[serde(default = "default_ipaddr", deserialize_with = "empty_string_as_none")]
pub src_addr: Option<IpAddr>,
#[serde(with = "ts_seconds")]
pub timestamp: DateTime<Utc>,
#[serde(rename = "type")]
pub kind: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct AtlasTracerouteHop {
#[serde(default)]
pub hop: u8,
pub error: Option<String>,
#[serde(default)]
pub result: Vec<AtlasTracerouteReply>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct AtlasTracerouteReply {
pub from: Option<IpAddr>,
#[serde(default)]
pub rtt: f64,
#[serde(default)]
pub size: u16,
#[serde(default)]
pub ttl: u8,
#[serde(skip)]
pub icmpext: Vec<AtlasIcmpExt>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct AtlasIcmpExt {
pub version: u8,
pub rfc4884: u8,
pub obj: Vec<AtlasIcmpExtObj>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct AtlasIcmpExtObj {
pub class: u8,
#[serde(rename = "type")]
pub kind: u8,
pub mpls: Vec<AtlasIcmpExtMplsData>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct AtlasIcmpExtMplsData {
pub label: u32,
pub exp: u8,
pub s: u8,
pub ttl: u8,
}
28 changes: 28 additions & 0 deletions src/atlas/reader.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use crate::atlas::models::AtlasTraceroute;
use crate::internal::models::TracerouteReply;
use std::io::{BufRead, Lines};

pub struct AtlasReader<R: BufRead> {
lines: Lines<R>,
}

impl<R: BufRead> AtlasReader<R> {
pub fn new(input: R) -> AtlasReader<R> {
AtlasReader {
lines: input.lines(),
}
}
}

impl<R: BufRead> Iterator for AtlasReader<R> {
type Item = Vec<TracerouteReply>;
fn next(&mut self) -> Option<Self::Item> {
match self.lines.next() {
Some(Ok(line)) => serde_json::from_str::<AtlasTraceroute>(&line)
.map(|t| Some(t.to_internal()))
.unwrap_or(None),
Some(Err(_)) => None,
None => None,
}
}
}
Loading

0 comments on commit e104712

Please sign in to comment.