Crate openvpn_plugin

source ·
Expand description

openvpn-plugin is a crate that makes it easy to write OpenVPN plugins in Rust.

The crate contains two main things:

  • The openvpn_plugin! macro for generating the FFI interface OpenVPN will interact with
  • The FFI and safe Rust types needed to communicate with OpenVPN.

Usage

Edit your Cargo.toml to depend on this crate and set the type of your crate to a cdylib in order to make it compile to a shared library that OpenVPN will understand:

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

[dependencies]
openvpn-plugin = "x.y"

In your crate root (lib.rs) define your handle type, the three callback functions and call the openvpn_plugin! macro to generate the corresponding FFI bindings. More details on the handle and the callback functions can be found in the documentation for the openvpn_plugin! macro.

use std::collections::HashMap;
use std::ffi::CString;
use std::io::Error;
use openvpn_plugin::{openvpn_plugin, EventResult, EventType};

pub struct Handle {
    // Fields needed for the plugin to keep state between callbacks
}

fn openvpn_open(
    args: Vec<CString>,
    env: HashMap<CString, CString>,
) -> Result<(Vec<EventType>, Handle), Error> {
    // Listen to only the `Up` event, which will be fired when a tunnel has been established.
    let events = vec![EventType::Up];
    // Create the handle instance.
    let handle = Handle { /* ... */ };
    Ok((events, handle))
}

fn openvpn_close(handle: Handle) {
    println!("Plugin is closing down");
}

fn openvpn_event(
    event: EventType,
    args: Vec<CString>,
    env: HashMap<CString, CString>,
    handle: &mut Handle,
) -> Result<EventResult, Error> {
    /* Process the event */

    // If the processing worked fine and/or the request the callback represents should be
    // accepted, return EventResult::Success. See EventResult docs for more info.
    Ok(EventResult::Success)
}

openvpn_plugin!(crate::openvpn_open, crate::openvpn_close, crate::openvpn_event, Handle);

Panic handling

C cannot handle Rust panic unwinding into it, so it is not good practice to let Rust panic when called from C. Because of this, all calls from this crate to the callbacks given to openvpn_plugin! ($open_fn, $close_fn and $event_fn) are wrapped in catch_unwind.

If catch_unwind captures a panic it will log it and then return OPENVPN_PLUGIN_FUNC_ERROR to OpenVPN.

Note that this will only work for unwinding panics, not with panic=abort.

Logging

Any errors returned from the user defined callbacks or panics that happens anywhere in Rust is logged by this crate before control is returned to OpenVPN. By default logging happens to stderr. To activate logging with the error! macro in the log crate, build this crate with the log feature.

Modules

  • FFI types and functions used by the plugin to convert between the types OpenVPN pass and expect back and the Rust types the plugin will be exposed to.

Macros

  • The main part of this crate. The macro generates the public FFI functions that OpenVPN looks for in a shared library:

Enums

  • Enum representing the results an OpenVPN plugin can return from an event callback.
  • All the events that an OpenVPN plugin can register for and get notified about. This is a Rust representation of the constants named OPENVPN_PLUGIN_* in openvpn-plugin.h.