#openvpn #vpn #cdylib #api-bindings #ffi #plugin

openvpn-plugin

A crate allowing easy creation of OpenVPN plugins in Rust

6 releases (3 breaking)

0.4.2 Feb 21, 2023
0.4.1 Jan 12, 2021
0.4.0 Nov 16, 2020
0.3.0 Oct 13, 2017
0.1.0 Jul 19, 2017

#1146 in Network programming

Download history 3423/week @ 2024-06-10 3695/week @ 2024-06-17 3285/week @ 2024-06-24 2304/week @ 2024-07-01 1733/week @ 2024-07-08 2410/week @ 2024-07-15 4473/week @ 2024-07-22 4201/week @ 2024-07-29 3542/week @ 2024-08-05 5149/week @ 2024-08-12 4376/week @ 2024-08-19 5028/week @ 2024-08-26 4437/week @ 2024-09-02 1768/week @ 2024-09-09 4170/week @ 2024-09-16 4318/week @ 2024-09-23

14,900 downloads per month

MIT/Apache

36KB
498 lines

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.

Dependencies

~1.5MB
~37K SLoC