Skip to content

Commit

Permalink
Port C FFI crate to maybenot-v2
Browse files Browse the repository at this point in the history
  • Loading branch information
hulthe committed Oct 22, 2024
1 parent 78de4bb commit b6bdd86
Show file tree
Hide file tree
Showing 11 changed files with 767 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
/Cargo.lock
*.a
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ resolver = "2"
members = [
# lib for the Maybenot framework
"crates/maybenot",
# ffi lib for the Maybenot framework
"crates/maybenot-ffi",
# simulator for the Maybenot framework
"crates/maybenot-simulator",
]
Expand All @@ -34,4 +36,4 @@ explicit_outlives_requirements = "warn"
missing_abi = "deny"
unused_lifetimes = "warn"
unused_macro_rules = "warn"
single_use_lifetimes = "warn"
single_use_lifetimes = "warn"
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ framework for creating defenses that hide such patterns.
The Maybenot workspace consists of the following crates:

- [maybenot](crates/maybenot): The core framework for creating defenses.
- [maybenot-ffi](crates/maybenot-ffi): A wrapper library around maybenot with a C FFI.
- [maybenot-simulator](crates/maybenot-simulator): A simulator for testing
defenses.

Expand Down
18 changes: 18 additions & 0 deletions crates/maybenot-ffi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "maybenot-ffi"
description = "An FFI wrapper around Maybenot"
version = "2.0.0"
edition.workspace = true
license.workspace = true
homepage.workspace = true
keywords.workspace = true
categories.workspace = true
repository.workspace = true

[lib]
crate-type = ["lib", "staticlib", "cdylib"]

[dependencies]
maybenot = { version = "2.0.0", path = "../maybenot" }
rand = "0.8.5"
rand_chacha = "0.3.1"
36 changes: 36 additions & 0 deletions crates/maybenot-ffi/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
CBINDGEN ?= cbindgen
CARGO ?= cargo
TARGET ?=
PROFILE ?= release
DESTINATION ?= .
CARGO_TARGET_DIR ?= ../../target

CARGO_OUTPUT_DIR := $(CARGO_TARGET_DIR)/$(TARGET)/$(PROFILE)
CARGOFLAGS += --target-dir $(CARGO_TARGET_DIR)

ifeq ($(PROFILE), release)
CARGOFLAGS += --release
endif

ifneq ($(TARGET),)
CARGOFLAGS += --target
CARGOFLAGS += $(TARGET)
endif

.PHONY: clean

# copy the library to the final destination, and strip the _ffi part
$(DESTINATION)/libmaybenot.a: $(CARGO_OUTPUT_DIR)/libmaybenot_ffi.a
cp $^ $@

# generate maybenot.h
maybenot.h: src/*.rs Cargo.toml cbindgen.toml
${CBINDGEN} -o maybenot.h

# build the static library
$(CARGO_OUTPUT_DIR)/libmaybenot_ffi.a: maybenot.h src/*.rs Cargo.toml cbindgen.toml
RUSTFLAGS="-C metadata=maybenot-ffi" ${CARGO} build $(CARGOFLAGS)

clean:
rm -f $(DESTINATION)/libmaybenot.a
${CARGO} clean $(CARGOFLAGS)
31 changes: 31 additions & 0 deletions crates/maybenot-ffi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Maybenot FFI

This crate contains C FFI bindings for Maybenot, which let's you use Maybenot as a static library
for languages other than Rust. Headers are found at `maybenot-ffi/maybenot.h` and are
auto-generated when compiling using `make`.

## Building
You need to have [rust](https://rustup.rs/) installed.
`cbindgen` is also required: `cargo install --force cbindgen`
Then just run `make` to build a static library at `maybenot-ffi/libmaybenot.a`.

Arguments to `make`, including default values:
- `DESTINATION=.` - the directory where the output artifacts will be placed.
- `TARGET=` override target architecture; cross-compile.
Use `rustup target` to list and install targets.
- `PROFILE=release` - override the cargo profile, valid options are `release` and `debug`.
- `CARGO=cargo` - path to cargo.
- `CBINDGEN=cbindgen` - path to cbindgen.
- `CARGO_TARGET_DIR=../../target` - the build directory.

Example:
```
make TARGET=x86_64-unknown-linux-gnu PROFILE=debug
```

In order to link the resulting library to your program, you'll need to explicitly link some
additional dependencies in addition to `-lmaybenot`.
Run the following command to get an up-to-date list of the required flags for your platform:
```
RUSTFLAGS="--print native-static-libs" cargo build
```
9 changes: 9 additions & 0 deletions crates/maybenot-ffi/cbindgen.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# See https://github.com/mozilla/cbindgen/blob/master/docs.md#cbindgentoml

language = "C"

autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
include_version = true

[enum]
prefix_with_name = true
229 changes: 229 additions & 0 deletions crates/maybenot-ffi/maybenot.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/* Generated with cbindgen:0.26.0 */

/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

enum MaybenotEventType {
MaybenotEventType_NormalRecv = 0,
MaybenotEventType_PaddingRecv = 1,
MaybenotEventType_TunnelRecv = 2,
MaybenotEventType_NormalSent = 3,
MaybenotEventType_PaddingSent = 4,
MaybenotEventType_TunnelSent = 5,
MaybenotEventType_BlockingBegin = 6,
MaybenotEventType_BlockingEnd = 7,
MaybenotEventType_TimerBegin = 8,
MaybenotEventType_TimerEnd = 9,
};
typedef uint32_t MaybenotEventType;

/**
* An FFI friendly result error code type.
*/
enum MaybenotResult {
/**
* Operation completed successfully
*/
MaybenotResult_Ok = 0,
/**
* The machine string wasn't valid UTF-8
*/
MaybenotResult_MachineStringNotUtf8 = 1,
/**
* Failed to parse machine string
*/
MaybenotResult_InvalidMachineString = 2,
/**
* Failed to start framework
*/
MaybenotResult_StartFramework = 3,
/**
* A null pointer was encountered
*/
MaybenotResult_NullPointer = 4,
};
typedef uint32_t MaybenotResult;

/**
* The different types of timers used by a [Machine].
*/
enum MaybenotTimer {
/**
* The scheduled timer for actions with a timeout.
*/
MaybenotTimer_Action = 0,
/**
* The machine's internal timer, updated by the machine using [MaybenotAction::UpdateTimer].
*/
MaybenotTimer_Internal = 1,
/**
* Apply to all timers.
*/
MaybenotTimer_All = 2,
};
typedef uint32_t MaybenotTimer;

/**
* A running Maybenot instance.
*
* - Create it: [maybenot_start].
* - Feed it actions: [maybenot_on_events].
* - Stop it: [maybenot_stop].
*/
typedef struct MaybenotFramework MaybenotFramework;

typedef struct MaybenotEvent {
MaybenotEventType event_type;
/**
* The ID of the machine that triggered the event, if any.
*/
uintptr_t machine;
} MaybenotEvent;

typedef struct MaybenotDuration {
/**
* Number of whole seconds
*/
uint64_t secs;
/**
* A nanosecond fraction of a second.
*/
uint32_t nanos;
} MaybenotDuration;

/**
* The action to be taken by the framework user.
*/
enum MaybenotAction_Tag {
/**
* Cancel the timer for a machine.
*/
MaybenotAction_Cancel = 0,
/**
* Schedule padding to be injected after the given timeout for a machine.
*/
MaybenotAction_SendPadding = 1,
/**
* Schedule blocking of outgoing traffic after the given timeout for a machine.
*/
MaybenotAction_BlockOutgoing = 2,
/**
* Update the timer duration for a machine.
*/
MaybenotAction_UpdateTimer = 3,
};
typedef uint32_t MaybenotAction_Tag;

typedef struct MaybenotAction_Cancel_Body {
/**
* The machine that generated the action.
*/
uintptr_t machine;
MaybenotTimer timer;
} MaybenotAction_Cancel_Body;

typedef struct MaybenotAction_SendPadding_Body {
/**
* The machine that generated the action.
*/
uintptr_t machine;
/**
* The time to wait before injecting a padding packet.
*/
struct MaybenotDuration timeout;
bool replace;
bool bypass;
} MaybenotAction_SendPadding_Body;

typedef struct MaybenotAction_BlockOutgoing_Body {
/**
* The machine that generated the action.
*/
uintptr_t machine;
/**
* The time to wait before blocking.
*/
struct MaybenotDuration timeout;
bool replace;
bool bypass;
/**
* How long to block.
*/
struct MaybenotDuration duration;
} MaybenotAction_BlockOutgoing_Body;

typedef struct MaybenotAction_UpdateTimer_Body {
uintptr_t machine;
struct MaybenotDuration duration;
bool replace;
} MaybenotAction_UpdateTimer_Body;

typedef struct MaybenotAction {
MaybenotAction_Tag tag;
union {
MaybenotAction_Cancel_Body cancel;
MaybenotAction_SendPadding_Body send_padding;
MaybenotAction_BlockOutgoing_Body block_outgoing;
MaybenotAction_UpdateTimer_Body update_timer;
};
} MaybenotAction;

/**
* Get the version of maybenot-ffi as a null terminated UTF-8-string.
*
* Example: `maybenot-ffi/1.0.1`
*/
const char *maybenot_version(void);

/**
* Start a new [`MaybenotFramework`] instance.
*
* # Safety
* - `machines_str` must be a null-terminated UTF-8 string, containing LF-separated machines.
* - `out` must be a valid pointer to some valid and aligned pointer-sized memory.
* - The pointer written to `out` is NOT safe to be used concurrently.
*/
MaybenotResult maybenot_start(const char *machines_str,
double max_padding_frac,
double max_blocking_frac,
struct MaybenotFramework **out);

/**
* Get the number of machines running in the [`MaybenotFramework`] instance.
*
* # Safety
* - `this` must have been created by [`maybenot_start`].
*/
uintptr_t maybenot_num_machines(struct MaybenotFramework *this_);

/**
* Stop a running [`MaybenotFramework`] instance. This will free the maybenot pointer.
*
* # Safety
* - `this` MUST have been created by [`maybenot_start`].
* - `this` MUST NOT be used after it has been passed to [`maybenot_stop`].
*/
void maybenot_stop(struct MaybenotFramework *this_);

/**
* Feed events to the [`MaybenotFramework`] instance.
*
* This may generate [super::MaybenotAction]s that will be written to `actions_out`.
* The number of actions will be written to `num_actions_out`.
*
* # Safety
* - `this` MUST have been created by [`maybenot_start`].
* - `events` MUST be a valid pointer to an array of size `num_events`.
* - `actions_out` MUST have capacity for [`maybenot_num_machines`] items of size
* `sizeof(MaybenotAction)` bytes.
* - `num_actions_out` MUST be a valid pointer where a 64bit int can be written.
*/
MaybenotResult maybenot_on_events(struct MaybenotFramework *this_,
const struct MaybenotEvent *events,
uintptr_t num_events,
struct MaybenotAction *actions_out,
uintptr_t *num_actions_out);
25 changes: 25 additions & 0 deletions crates/maybenot-ffi/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/// An FFI friendly result error code type.
#[repr(u32)]
#[derive(Debug, Clone, Copy)]
pub enum MaybenotResult {
/// Operation completed successfully
Ok = 0,

/// The machine string wasn't valid UTF-8
MachineStringNotUtf8 = 1,

/// Failed to parse machine string
InvalidMachineString = 2,

/// Failed to start framework
StartFramework = 3,

/// A null pointer was encountered
NullPointer = 4,
}

impl<T> From<Result<T, MaybenotResult>> for MaybenotResult {
fn from(result: Result<T, MaybenotResult>) -> Self {
result.map(|_| MaybenotResult::Ok).unwrap_or_else(|err| err)
}
}
Loading

0 comments on commit b6bdd86

Please sign in to comment.