openvpn needs a proto handler. Here it is.
Removed all of the up/down scripts from the init handler
and made those entirely optional (with some ucode examples).
The config options have been updated to reflect v 2.6/2.7,
with a 'd' flag to denote deprecated. Deprecated flags are
gated behind an 'allow_deprecated' config flag, which must
be on to use them. Some flags will cease to work in the next
version.
Users should not be using compression. Openvpn has enough
security holes and pitfalls already without using
compression.
Updated the example configs (left in place as legacy
documentation) and removed older cryptos which do not exist
in ovpn any longer.
A migration script is included -x. /etc/config/openvpn
entries become interface entries in /etc/config/network
with proto='openvpn'. The source config is retained.
Signed-off-by: Paul Donald <redacted>
https://github.com/openwrt/packages/pull/28533
PKG_NAME:=openvpn
PKG_VERSION:=2.6.14
-PKG_RELEASE:=4
+PKG_RELEASE:=5
PKG_SOURCE_URL:=\
https://build.openvpn.net/downloads/releases/ \
)
endef
-define Package/openvpn-$(BUILD_VARIANT)/conffiles
-/etc/config/openvpn
-/etc/openvpn.user
-endef
-
define Package/openvpn-$(BUILD_VARIANT)/install
$(INSTALL_DIR) \
$(1)/usr/sbin \
$(1)/etc/init.d \
$(1)/etc/config \
$(1)/etc/openvpn \
+ $(1)/etc/uci-defaults \
$(1)/lib/functions \
+ $(1)/lib/netifd/proto \
$(1)/lib/upgrade/keep.d \
$(1)/usr/libexec \
$(1)/etc/hotplug.d/openvpn
$(1)/usr/sbin/
$(INSTALL_BIN) \
- files/openvpn.init \
- $(1)/etc/init.d/openvpn
+ files/lib/netifd/proto/openvpn.sh \
+ $(1)/lib/netifd/proto/
$(INSTALL_BIN) \
- files/usr/libexec/openvpn-hotplug \
- $(1)/usr/libexec/openvpn-hotplug
-
- $(INSTALL_DATA) \
- files/lib/functions/openvpn.sh \
- $(1)/lib/functions/openvpn.sh
-
- $(INSTALL_DATA) \
- files/etc/hotplug.d/openvpn/01-user \
- $(1)/etc/hotplug.d/openvpn/01-user
-
- $(INSTALL_DATA) \
- files/etc/openvpn.user \
- $(1)/etc/openvpn.user
+ files/etc/uci-defaults/60_openvpn_migrate.sh \
+ $(1)/etc/uci-defaults/
$(INSTALL_DATA) \
files/openvpn.options \
$(1)/usr/share/openvpn/openvpn.options
- $(INSTALL_CONF) files/openvpn.config \
- $(1)/etc/config/openvpn
+ $(INSTALL_BIN) \
+ files/up.uc files/down.uc files/route-pre-down.uc files/route-up.uc \
+ files/ipchange.uc \
+ files/client-connect.uc files/client-disconnect.uc files/client-crresponse.uc \
+ files/auth-user-pass-verify.uc files/tls-verify.uc \
+ $(1)/usr/share/openvpn/
$(INSTALL_DATA) \
- files/openvpn.upgrade \
+ files/openvpn.sysupgrade \
$(1)/lib/upgrade/keep.d/openvpn
endef
--- /dev/null
+#!/usr/bin/env ucode
+
+// cmd method { via-env|via-file }
+
+print('cmd ', ARGV, '\n');
+
+// See the environment variables passed to this script: https://ucode.mein.io/module-core.html#getenv
+print(getenv(), '\n');
+
+// https://openvpn.net/community-docs/community-articles/openvpn-2-6-manual.html#environmental-variables-177179
+// https://openvpn.net/community-docs/community-articles/openvpn-2-6-manual.html#script-hooks-177179
+
+
+/* do something */
+
+
+/*
+exit(0); // client auth request accepted
+exit(1); // reject client
+exit(2); // deferred auth
+*/
--- /dev/null
+#!/usr/bin/env ucode
+
+// cmd [args]
+
+print('cmd ', ARGV, '\n');
+
+// See the environment variables passed to this script: https://ucode.mein.io/module-core.html#getenv
+print(getenv(), '\n');
+
+// https://openvpn.net/community-docs/community-articles/openvpn-2-6-manual.html#environmental-variables-177179
+// https://openvpn.net/community-docs/community-articles/openvpn-2-6-manual.html#script-hooks-177179
+
+
+/* do something */
+
+
+// If script returns a non-zero error status, it will cause the client to be disconnected.
+exit(0);
--- /dev/null
+#!/usr/bin/env ucode
+
+import { readfile } from 'fs';
+
+// cmd client_response_temp_file
+
+print('cmd ', ARGV, '\n');
+
+// See the environment variables passed to this script: https://ucode.mein.io/module-core.html#getenv
+print(getenv(), '\n');
+
+// https://openvpn.net/community-docs/community-articles/openvpn-2-6-manual.html#script-hooks-177179
+// https://openvpn.net/community-docs/community-articles/openvpn-2-6-manual.html#environmental-variables-177179
+
+const content = trim(readfile(ARGV[0]));
+
+const reply = b64dec(content);
+
+
+
+/* do something */
+
+
+// write result to the `auth_control_file` filename stored in the environment.
+
--- /dev/null
+#!/usr/bin/env ucode
+
+// cmd [args]
+
+print('cmd ', ARGV, '\n');
+
+// See the environment variables passed to this script: https://ucode.mein.io/module-core.html#getenv
+print(getenv(), '\n');
+
+// https://openvpn.net/community-docs/community-articles/openvpn-2-6-manual.html#script-hooks-177179
+// https://openvpn.net/community-docs/community-articles/openvpn-2-6-manual.html#environmental-variables-177179
+
+
+/* do something */
+
--- /dev/null
+#!/usr/bin/env ucode
+
+// 0 == link_mtu => historic
+// cmd tun_dev tun_mtu 0 ifconfig_local_ip ifconfig_remote_ip [init | restart]
+// cmd tap_dev tap_mtu 0 ifconfig_local_ip ifconfig_netmask [init | restart]
+
+print('tun_dev ', ARGV[0],' tun_mtu ', ARGV[1], ' ', ARGV[2], ' if_lip "', ARGV[3], '" if_rip "', ARGV[4], '" i/r "', ARGV[5], '"\n');
+print('tap_dev ', ARGV[0],' tap_mtu ', ARGV[1], ' ', ARGV[2], ' if_lip "', ARGV[3], '" if_nm "', ARGV[4], '" i/r "', ARGV[5], '"\n');
+
+/* e.g.
+tun_dev asdf0 tun_mtu 1500 0 if_lip "" if_rip "" i/r "init"
+*/
+
+// See the environment variables passed to this script: https://ucode.mein.io/module-core.html#getenv
+print(getenv(), '\n');
+
+// https://openvpn.net/community-docs/community-articles/openvpn-2-6-manual.html#environmental-variables-177179
+// https://openvpn.net/community-docs/community-articles/openvpn-2-6-manual.html#script-hooks-177179
+
+/* e.g.
+{
+ "script_type": "down",
+ "dev_type": "tun",
+ "dev": "asdf0",
+ "tun_mtu": "1500",
+ "script_context": "init",
+ "verb": "6",
+ "daemon": "0",
+ "daemon_log_redirect": "0",
+ "daemon_start_time": "1770389648",
+ "daemon_pid": "6116",
+ "proto_1": "udp",
+ "local_port_1": "1194",
+ "remote_1": "192.0.2.10",
+ "remote_port_1": "1194"
+}
+*/
+++ /dev/null
-#!/bin/sh
-
-[ -e "/etc/openvpn.user" ] && {
- env -i ACTION="$ACTION" INSTANCE="$INSTANCE" \
- /bin/sh \
- /etc/openvpn.user \
- $*
-}
-
-# Wrap user defined scripts on up/down/route-up/route-pre-down/ipchange events
-# Scriptp set with up/down/route-up/route-pre-down/ipchange in the openvpn config are also executed with the command=user_xxxx
-case "$ACTION" in
- up) command=$user_up ;;
- down) command=$user_down ;;
- route-up) command=$user_route_up ;;
- route-pre-down) command=$user_route_pre_down ;;
- ipchange) command=$user_ipchange ;;
- *) command= ;;
-esac
-
-if [ -n "$command" ]; then
- shift
- exec /bin/sh -c "$command $*"
-fi
-
-exit 0
-
+++ /dev/null
-#!/bin/sh
-#
-# This file is interpreted as shell script.
-# Put your custom openvpn action here, they will
-# be executed with each opevnp event.
-#
-# $ACTION
-# <down> down action is generated after the TUN/TAP device is closed
-# <up> up action is generated after the TUN/TAP device is opened
-# $INSTANCE Name of the openvpn instance which went up or down
-
--- /dev/null
+#!/bin/sh
+
+OPENVPN_PKG="openvpn"
+NETWORK_PKG="network"
+
+# Exit if no openvpn config exists
+uci -q show "$OPENVPN_PKG" >/dev/null || exit 0
+
+uci batch <<EOF
+$(
+
+# Find named openvpn sections
+uci show "$OPENVPN_PKG" | \
+sed -n "s/^$OPENVPN_PKG\.\\([^=]*\\)=openvpn$/\\1/p" | \
+while read -r sec; do
+ iface="$sec"
+
+ # Skip if interface already exists
+ uci -q get $NETWORK_PKG.$iface >/dev/null && continue
+
+ # Create interface in network
+ echo "set $NETWORK_PKG.$iface=interface"
+ # Set the interface protocol to 'openvpn'
+ echo "set $NETWORK_PKG.$iface.proto='openvpn'"
+
+ # Copy options, skipping the section header
+ uci show "$OPENVPN_PKG.$sec" | \
+ while IFS='=' read -r key val; do
+ case "$key" in
+ # section declaration: openvpn.vpn0=openvpn
+ "$OPENVPN_PKG.$sec") continue ;;
+ "$OPENVPN_PKG.$sec.proto")
+ echo "set $NETWORK_PKG.$iface.ovpnproto=$val"
+ continue
+ ;;
+ esac
+
+ opt="${key##*.}"
+
+ echo "set $NETWORK_PKG.$iface.$opt=$val"
+ done
+done
+
+echo "commit $NETWORK_PKG"
+)
+EOF
+
+exit 0
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env ucode
+
+// cmd ip address port number
+
+print('cmd ', ARGV[0],' ip ', ARGV[1], ' address ', ARGV[2], ' port ', ARGV[3], ' number ', ARGV[4], '"\n');
+
+// See the environment variables passed to this script: https://ucode.mein.io/module-core.html#getenv
+print(getenv(), '\n');
+
+// https://openvpn.net/community-docs/community-articles/openvpn-2-6-manual.html#environmental-variables-177179
+
+++ /dev/null
-#!/bin/sh
-
-get_openvpn_option() {
- local config="$1"
- local variable="$2"
- local option="$3"
-
- local value="$(sed -rne 's/^[ \t]*'"$option"'[ \t]+'"'([^']+)'"'[ \t]*$/\1/p' "$config" | tail -n1)"
- [ -n "$value" ] || value="$(sed -rne 's/^[ \t]*'"$option"'[ \t]+"(([^"\\]|\\.)+)"[ \t]*$/\1/p' "$config" | tail -n1 | sed -re 's/\\(.)/\1/g')"
- [ -n "$value" ] || value="$(sed -rne 's/^[ \t]*'"$option"'[ \t]+(([^ \t\\]|\\.)+)[ \t]*$/\1/p' "$config" | tail -n1 | sed -re 's/\\(.)/\1/g')"
- [ -n "$value" ] || return 1
-
- export -n "$variable=$value"
- return 0
-}
-
--- /dev/null
+#!/bin/sh
+# OpenVPN netifd proto handler for OpenWrt
+# Copyright (C) 2026
+# shellcheck disable=SC1091,2046,2091,3043,3060
+
+[ -x /usr/sbin/openvpn ] || exit 0
+
+[ -n "$INCLUDE_ONLY" ] || {
+ . /lib/functions.sh
+ . /usr/share/openvpn/openvpn.options # OPENVPN_* options
+ . ../netifd-proto.sh
+ . /usr/share/libubox/jshn.sh
+ init_proto "$@"
+}
+
+# Helper to DRY up repeated option handling in init/setup
+option_builder() {
+ # option_builder <action:add|build> <LIST_VAR_NAME> <type>
+ local action="$1"; shift
+ local list_var="$1"; shift
+ local opt_type="$1"; shift
+ local f v
+
+ for f in $(eval echo \$"$list_var")
+ do
+ f=${f%%:*}
+
+ if [ "$action" = "add" ]; then
+ case "$opt_type" in
+ bool) proto_config_add_boolean "$f:bool" ;;
+ protobool) proto_config_add_boolean "$f:protobool" ;;
+ uinteger) proto_config_add_int "$f:uinteger" ;;
+ integer) proto_config_add_int "$f:integer" ;;
+ string) proto_config_add_string "$f:string" ;;
+ protostring) proto_config_add_string "$f:protostring" ;;
+ file) proto_config_add_string "$f:file" ;;
+ list) proto_config_add_array "$f:list" ;;
+ esac
+ elif [ "$action" = "build" ]; then
+ [ "${f#*:}" = "d" ] && [ "$allow_deprecated" = 0 ] && continue
+ case "$opt_type" in
+ bool)
+ json_get_var v "$f"
+ [ "$v" = 1 ] && append exec_params " --${f//_/-}"
+ ;;
+ uinteger|integer|string)
+ json_get_var v "$f"
+ [ -n "$v" ] && append exec_params " --${f//_/-} $v"
+ ;;
+ file)
+ json_get_var v "$f"
+ [ -f "$v" ] || continue
+ [ -n "$v" ] && append exec_params " --${f//_/-} $v"
+ ;;
+ list)
+ json_get_values v "$f"
+ [ -n "${v}" ] && append exec_params "$(for d in $v; do echo " --${f//_/-} $d"; done)"
+ ;;
+ esac
+ fi
+ done
+}
+
+
+# Not real config params used by openvpn - only by our proto handler
+PROTO_BOOLS='
+allow_deprecated
+'
+
+PROTO_STRINGS='
+username
+password
+cert_password
+'
+
+proto_openvpn_init_config() {
+ available=1
+ no_device=1
+ lasterror=1
+ renew_handler=1
+
+ # There may be opvnvpn options which mean that a tap L2 device exists.
+ # TODO: Set no_device to depend on tap device
+
+ proto_add_dynamic_defaults
+
+ # Add proto config options - netifd compares these for changes between interface events
+ option_builder add PROTO_BOOLS protobool
+ option_builder add PROTO_STRINGS string
+ option_builder add OPENVPN_BOOLS bool
+ option_builder add OPENVPN_UINTS uinteger
+ option_builder add OPENVPN_INTS integer
+ option_builder add OPENVPN_PARAMS_STRING string
+ option_builder add OPENVPN_PARAMS_FILE file
+ option_builder add OPENVPN_LIST list
+
+}
+
+
+proto_openvpn_setup() {
+ local config="$1"
+ local allow_deprecated exec_params
+ allow_deprecated=0
+
+ exec_params=
+
+ json_get_var allow_deprecated allow_deprecated
+
+ # Build exec params from configured options we get from ubus values stored during init_config
+ option_builder build OPENVPN_BOOLS bool
+ option_builder build OPENVPN_UINTS uinteger
+ option_builder build OPENVPN_INTS integer
+ option_builder build OPENVPN_PARAMS_STRING string
+ option_builder build OPENVPN_PARAMS_FILE file
+ option_builder build OPENVPN_LIST list
+
+ proto_add_dynamic_defaults
+
+ json_get_var username username
+ json_get_var password password
+ json_get_var cert_password cert_password
+ json_get_var config_file config
+
+ mkdir -p /var/run
+ # combine into --askpass:
+ if [ -n "$cert_password" ]; then
+ cp_file="/var/run/openvpn.$config.pass"
+ umask 077
+ printf '%s\n' "${cert_password:-}" > "$cp_file"
+ umask 022
+ append exec_params " --askpass $cp_file"
+ elif [ -n "$askpass" ]; then
+ append exec_params " --askpass $askpass"
+ fi
+
+ # combine into --auth-user-pass:
+ if [ -n "$username" ] || [ -n "$password" ]; then
+ auth_file="/var/run/openvpn.$config.auth"
+ umask 077
+ printf '%s\n' "${username:-}" "${password:-}" > "$auth_file"
+ umask 022
+ append exec_params " --auth-user-pass $auth_file"
+ elif [ -n "$auth_user_pass" ]; then
+ auth_file="$auth_user_pass"
+ fi
+
+ # shellcheck disable=SC2154
+ cd_dir="${config_file%/*}"
+ [ "$cd_dir" = "$config_file" ] && cd_dir="/"
+
+ # Testing option
+ # ${tls_exit:+--tls-exit} \
+
+ json_get_var dev_type dev_type
+ # shellcheck disable=SC2086
+ proto_run_command "$config" openvpn \
+ $([ -z "$dev_type" ] && echo " --dev-type tun") \
+ --cd "$cd_dir" \
+ --status "/var/run/openvpn.$config.status" \
+ --syslog "openvpn_$config" \
+ --tmp-dir "/var/run" \
+ $exec_params
+
+ # last param wins; user provided status or syslog supersedes these.
+
+}
+
+proto_openvpn_renew() {
+ config="$1"
+ local sigusr1
+
+ sigusr1="$(kill -l SIGUSR1)"
+ [ -n "$sigusr1" ] && proto_kill_command "$config" "$sigusr1"
+
+}
+
+proto_openvpn_teardown() {
+ local iface="$1"
+ rm -f \
+ "/var/run/openvpn.$iface.pass" \
+ "/var/run/openvpn.$iface.auth" \
+ "/var/run/openvpn.$iface.status"
+ proto_kill_command "$iface"
+}
+
+
+[ -n "$INCLUDE_ONLY" ] || {
+ add_protocol openvpn
+}
-package openvpn
+package network
#################################################
# Sample to include a custom config file. #
#################################################
-config openvpn custom_config
+config interface custom_config
- # Set to 1 to enable this instance:
- option enabled 0
+ option proto 'openvpn'
+
+ # Set to 1 to disable this interface:
+ option disabled 0
# Credentials to login
- #option username 'login'
- #option password 'password'
+# option username 'login'
+# option password 'password'
# Password for client certificate
- #option cert_password 'cert_password'
+# option cert_password 'cert_password'
# Include OpenVPN configuration
option config /etc/openvpn/my-vpn.conf
# multi-client server. #
#################################################
-config openvpn sample_server
+config interface sample_server
+
+ option proto 'openvpn'
- # Set to 1 to enable this instance:
- option enabled 0
+ # Set to 1 to disable this interface:
+ option disabled 0
# Which local IP address should OpenVPN
# listen on? (optional)
+
# option local 0.0.0.0
# Which TCP/UDP port should OpenVPN listen on?
# on the same machine, use a different port
# number for each one. You will need to
# open up this port on your firewall.
+
option port 1194
# TCP or UDP server?
+
# option proto tcp
option proto udp
- # "dev tun" will create a routed IP tunnel,
- # "dev tap" will create an ethernet tunnel.
- # Use "dev tap0" if you are ethernet bridging
- # and have precreated a tap0 virtual interface
- # and bridged it with your ethernet interface.
+ # "dev_type tun" will create a routed IP tunnel,
+ # "dev_type tap" will create an Ethernet tunnel.
+ # Use "dev tap0" if you are Ethernet bridging
+ # and have created a tap0 virtual interface
+ # and bridged it with your Ethernet interface.
# If you want to control access policies
# over the VPN, you must create firewall
# rules for the the TUN/TAP interface.
# On most systems, the VPN will not function
# unless you partially or fully disable
# the firewall for the TUN/TAP interface.
-# option dev tap
- option dev tun
+
+# option dev_type tap
+ option dev_type tun
# SSL/TLS root certificate (ca), certificate
# (cert), and private key (key). Each client
# Any X509 key management system can be used.
# OpenVPN can also use a PKCS #12 formatted key file
# (see "pkcs12" directive in man page).
+
option ca /etc/openvpn/ca.crt
option cert /etc/openvpn/server.crt
+
# This file should be kept secret:
+
option key /etc/openvpn/server.key
- # Diffie hellman parameters.
+ # Diffie-Hellman parameters.
# Generate your own with:
# openssl dhparam -out dh2048.pem 2048
# Substitute 2048 for 1024 if you are using
# 1024 bit keys.
+
option dh /etc/openvpn/dh2048.pem
# Configure server mode and supply a VPN subnet
# the rest will be made available to clients.
# Each client will be able to reach the server
# on 10.8.0.1. Comment this line out if you are
- # ethernet bridging. See the man page for more info.
+ # Ethernet bridging. See the man page for more info.
+
option server "10.8.0.0 255.255.255.0"
# Maintain a record of client <-> virtual IP address
# is restarted, reconnecting clients can be assigned
# the same virtual IP address from the pool that was
# previously assigned.
+
option ifconfig_pool_persist /tmp/ipp.txt
- # Configure server mode for ethernet bridging.
+ # Configure server mode for Ethernet bridging.
# You must first use your OS's bridging capability
- # to bridge the TAP interface with the ethernet
+ # to bridge the TAP interface with the Ethernet
# NIC interface. Then you must manually set the
- # IP/netmask on the bridge interface, here we
+ # IP/net-mask on the bridge interface, here we
# assume 10.8.0.4/255.255.255.0. Finally we
# must set aside an IP range in this subnet
# (start=10.8.0.50 end=10.8.0.100) to allocate
# to connecting clients. Leave this line commented
- # out unless you are ethernet bridging.
+ # out unless you are Ethernet bridging.
+
# option server_bridge "10.8.0.4 255.255.255.0 10.8.0.50 10.8.0.100"
# Push routes to the client to allow it
# private subnets will also need
# to know to route the OpenVPN client
# address pool (10.8.0.0/255.255.255.0)
- # back to the OpenVPN server.
+ # back to the OpenVPN server:
+
# list push "route 192.168.10.0 255.255.255.0"
# list push "route 192.168.20.0 255.255.255.0"
# also has a small subnet behind his connecting
# machine, such as 192.168.40.128/255.255.255.248.
# First, uncomment out these lines:
+
# option client_config_dir /etc/openvpn/ccd
# list route "192.168.40.128 255.255.255.248"
+
# Then create a file ccd/Thelonious with this line:
- # iroute 192.168.40.128 255.255.255.248
+
+# iroute 192.168.40.128 255.255.255.248
+
# This will allow Thelonious' private subnet to
# access the VPN. This example will only work
# if you are routing, not bridging, i.e. you are
# EXAMPLE: Suppose you want to give
# Thelonious a fixed VPN IP address of 10.9.0.1.
- # First uncomment out these lines:
+ # First uncomment these lines:
+
# option client_config_dir /etc/openvpn/ccd
# list route "10.9.0.0 255.255.255.252"
# list route "192.168.100.0 255.255.255.0"
+
+
# Then add this line to ccd/Thelonious:
- # ifconfig-push "10.9.0.1 10.9.0.2"
+
+ #ifconfig-push "10.9.0.1 10.9.0.2"
# Suppose that you want to enable different
# firewall access policies for different groups
# modify the firewall in response to access
# from different clients. See man
# page for more info on learn-address script.
+
# option learn_address /etc/openvpn/script
# If enabled, this directive will configure
# client's local DHCP server is reachable via
# a more specific route than the default route
# of 0.0.0.0/0.0.0.0.
+
# list push "redirect-gateway"
# Certain Windows-specific network settings
# can be pushed to clients, such as DNS
# or WINS server addresses. CAVEAT:
# http://openvpn.net/faq.html#dhcpcaveats
+
# list push "dhcp-option DNS 10.8.0.1"
# list push "dhcp-option WINS 10.8.0.1"
# To force clients to only see the server, you
# will also need to appropriately firewall the
# server's TUN/TAP interface.
+
# option client_to_client 1
# Uncomment this directive if multiple clients
# IF YOU HAVE NOT GENERATED INDIVIDUAL
# CERTIFICATE/KEY PAIRS FOR EACH CLIENT,
# EACH HAVING ITS OWN UNIQUE "COMMON NAME",
- # UNCOMMENT THIS LINE OUT.
+ # UNCOMMENT THIS LINE.
+
# option duplicate_cn 1
# The keepalive directive causes ping-like
# Ping every 10 seconds, assume that remote
# peer is down if no ping received during
# a 120 second time period.
+
option keepalive "10 120"
# For extra security beyond that provided
# The second parameter should be '0'
# on the server and '1' on the clients.
# This file is secret:
+
# option tls_auth "/etc/openvpn/ta.key 0"
# For additional privacy, a shared secret key
# tls_auth and tls_crypt should NOT
# be combined, as tls_crypt implies tls_auth.
# Use EITHER tls_crypt, tls_auth, or neither option.
+
# option tls_crypt "/etc/openvpn/ta.key"
# Set the minimum required TLS protocol version
# for all connections.
#
- # Require at least TLS 1.1
-# option tls_version_min "1.1"
- # Require at least TLS 1.2
+ # Require at least TLS 1.2:
+
# option tls_version_min "1.2"
+
# Require TLS 1.2, or the highest version supported
- # on the system
+ # on the system:
+
# option tls_version_min "1.2 'or-highest'"
# List the preferred ciphers to use for the data channel.
# Run openvpn --show-ciphers to see all supported ciphers.
+
# list data_ciphers 'AES-256-GCM'
# list data_ciphers 'AES-128-GCM'
# list data_ciphers 'CHACHA20-POLY1305'
# peers that do not support cipher negotiation.
#
# Use AES-256-CBC as fallback
+
# option data_ciphers_fallback 'AES-128-CBC'
- # Use AES-128-CBC as fallback
+
+ # Use AES-128-CBC as fallback:
+
# option data_ciphers_fallback 'AES-256-CBC'
- # Use Triple-DES as fallback
-# option data_ciphers_fallback 'DES-EDE3-CBC'
- # Use BF-CBC as fallback
-# option data_ciphers_fallback 'BF-CBC'
-
- # OpenVPN versions 2.4 and later will attempt to
- # automatically negotiate the most secure cipher
- # between the client and server, regardless of a
- # configured "option cipher" (see below).
- # Automatic negotiation is recommended.
- #
- # Uncomment this option to disable this behavior,
- # and force all OpenVPN peers to use the configured
- # cipher option instead (not recommended).
-# option ncp_disable
-
- # Enable compression on the VPN link.
- # If you enable it here, you must also
- # enable it in the client config file.
- #
- # Compression is not recommended, as compression and
- # encryption in combination can weaken the security
- # of the connection.
- #
- # LZ4 requires OpenVPN 2.4+ client and server
-# option compress lz4
- # LZO is available by default only in openvpn-openssl variant
- # LZO is compatible with most OpenVPN versions
-# option compress lzo
-
- # Control how OpenVPN handles peers using compression
- #
- # Do not allow any connections using compression
-# option allow_compression 'no'
- # Allow incoming compressed packets, but do not send compressed packets to other peers
- # This can be useful when migrating old configurations with compression activated
-# option allow_compression 'asym'
- # Both incoming and outgoing packets may be compressed
-# option allow_compression 'yes'
# The maximum number of concurrently connected
# clients we want to allow.
+
# option max_clients 100
# The persist options will try to avoid
# "log" will truncate the log file on OpenVPN startup,
# while "log-append" will append to it. Use one
# or the other (but not both).
-# option log /tmp/openvpn.log
-# option log_append /tmp/openvpn.log
+
+ # option log /tmp/openvpn.log
+ # option log_append /tmp/openvpn.log
# Set the appropriate level of log
# file verbosity.
# Silence repeating messages. At most 20
# sequential messages of the same message
- # category will be output to the log.
+ # category will be output to the log:
+
# option mute 20
# for connecting to multi-client server. #
##############################################
-config openvpn sample_client
+config interface sample_client
- # Set to 1 to enable this instance:
- option enabled 0
+ option proto 'openvpn'
+
+ # Set to 1 to disable this interface:
+ option disabled 0
# Specify that we are a client and that we
# will be pulling certain config file directives
# On most systems, the VPN will not function
# unless you partially or fully disable
# the firewall for the TUN/TAP interface.
+
# option dev tap
option dev tun
# Are we connecting to a TCP or
# UDP server? Use the same setting as
# on the server.
+
# option proto tcp
option proto udp
# The hostname/IP and port of the server.
# You can have multiple remote entries
# to load balance between the servers.
+
list remote "my_server_1 1194"
# list remote "my_server_2 1194"
# Choose a random host from the remote
# list for load_balancing. Otherwise
# try hosts in the order specified.
+
# option remote_random 1
# Keep trying indefinitely to resolve the
# if your proxy server requires
# authentication.
# retry on connection failures:
+
# option http_proxy_retry 1
+
# specify http proxy address and port:
+
# option http_proxy "192.168.1.100 8080"
# Wireless networks often produce a lot
# a separate .crt/.key file pair
# for each client. A single ca
# file can be used for all clients.
+
option ca /etc/openvpn/ca.crt
option cert /etc/openvpn/client.crt
option key /etc/openvpn/client.key
# your server certificates with the nsCertType
# field set to "server". The build_key_server
# script in the easy_rsa folder will do this.
+
# option remote_cert_tls server
# If a tls_auth key is used on the server
# then every client must also have the key.
+
# option tls_auth "/etc/openvpn/ta.key 1"
# If a tls_crypt key is used on the server
# every client must also have the key.
+
# option tls_crypt "/etc/openvpn/ta.key"
# Set the minimum required TLS protocol version
# for all connections.
- #
- # Require at least TLS 1.1
-# option tls_version_min "1.1"
# Require at least TLS 1.2
+
# option tls_version_min "1.2"
+
# Require TLS 1.2, or the highest version supported
# on the system
+
# option tls_version_min "1.2 'or-highest'"
- # List the preferred ciphers for the data channel.
+ # List the preferred ciphers for the data channel":
+
# list data_ciphers 'AES-256-GCM'
# list data_ciphers 'AES-128-GCM'
# list data_ciphers 'CHACHA20-POLY1305'
# option data_ciphers_fallback 'AES-128-CBC'
# Use AES-128-CBC as fallback
# option data_ciphers_fallback 'AES-256-CBC'
- # Use Triple-DES as fallback
-# option data_ciphers_fallback 'DES-EDE3-CBC'
- # Use BF-CBC as fallback
-# option data_ciphers_fallback 'BF-CBC'
-
- # Enable compression on the VPN link.
- # Don't enable this unless it is also
- # enabled in the server config file.
- #
- # Compression is not recommended, as compression and
- # encryption in combination can weaken the security
- # of the connection.
- #
- # LZ4 requires OpenVPN 2.4+ on server and client
-# option compress lz4
- # LZO is available by default only in openvpn-openssl variant
- # LZO is compatible with most OpenVPN versions
-# option compress lzo
# Set log file verbosity.
option verb 3
+++ /dev/null
-#!/bin/sh /etc/rc.common
-# Copyright (C) 2008-2013 OpenWrt.org
-# Copyright (C) 2008 Jo-Philipp Wich
-# This is free software, licensed under the GNU General Public License v2.
-# See /LICENSE for more information.
-
-START=90
-STOP=10
-
-USE_PROCD=1
-PROG=/usr/sbin/openvpn
-
-PATH_INSTANCE_DIR="/etc/openvpn"
-LIST_SEP="
-"
-
-UCI_STARTED=
-UCI_DISABLED=
-
-append_param() {
- local s="$1"
- local v="$2"
- case "$v" in
- *_*_*_*) v=${v%%_*}-${v#*_}; v=${v%%_*}-${v#*_}; v=${v%%_*}-${v#*_} ;;
- *_*_*) v=${v%%_*}-${v#*_}; v=${v%%_*}-${v#*_} ;;
- *_*) v=${v%%_*}-${v#*_} ;;
- esac
- echo -n "$v" >> "/var/etc/openvpn-$s.conf"
- return 0
-}
-
-append_bools() {
- local p; local v; local s="$1"; shift
- for p in $*; do
- config_get_bool v "$s" "$p"
- [ "$v" = 1 ] && append_param "$s" "$p" && echo >> "/var/etc/openvpn-$s.conf"
- done
-}
-
-append_params() {
- local p; local v; local s="$1"; shift
- for p in $*; do
- config_get v "$s" "$p"
- IFS="$LIST_SEP"
- for v in $v; do
- [ "$v" = "frames_only" ] && [ "$p" = "compress" ] && unset v && append_param "$s" "$p" && echo >> "/var/etc/openvpn-$s.conf"
- [ -n "$v" ] && [ "$p" != "push" ] && append_param "$s" "$p" && echo " $v" >> "/var/etc/openvpn-$s.conf"
- [ -n "$v" ] && [ "$p" = "push" ] && append_param "$s" "$p" && echo " \"$v\"" >> "/var/etc/openvpn-$s.conf"
- done
- unset IFS
- done
-}
-
-append_list() {
- local p; local v; local s="$1"; shift
-
- list_cb_append() {
- v="${v}:$1"
- }
-
- for p in $*; do
- unset v
- config_list_foreach "$s" "$p" list_cb_append
- [ -n "$v" ] && append_param "$s" "$p" && echo " ${v:1}" >> "/var/etc/openvpn-$s.conf"
- done
-}
-
-section_enabled() {
- config_get_bool enable "$1" 'enable' 0
- config_get_bool enabled "$1" 'enabled' 0
- [ $enable -gt 0 ] || [ $enabled -gt 0 ]
-}
-
-create_temp_file() {
- mkdir -p "$(dirname "$1")"
- rm -f "$1"
- touch "$1"
- chown root "$1"
- chmod 0600 "$1"
-}
-
-openvpn_get_dev() {
- local dev dev_type
- local name="$1"
- local conf="$2"
-
- # Do override only for configurations with config_file
- config_get config_file "$name" config
- [ -n "$config_file" ] || return
-
- # Check there is someething to override
- config_get dev "$name" dev
- config_get dev_type "$name" dev_type
- [ -n "$dev" ] || return
-
- # If there is a no dev_type, try to guess it
- if [ -z "$dev_type" ]; then
- . /lib/functions/openvpn.sh
-
- local odev odev_type
- get_openvpn_option "$conf" odev dev
- get_openvpn_option "$conf" odev_type dev-type
- [ -n "$odev_type" ] || odev_type="$odev"
-
- case "$odev_type" in
- tun*) dev_type="tun" ;;
- tap*) dev_type="tap" ;;
- *) return;;
- esac
- fi
-
- # Return overrides
- echo "--dev-type $dev_type --dev $dev"
-}
-
-openvpn_get_credentials() {
- local name="$1"
- local ret=""
-
- config_get cert_password "$name" cert_password
- config_get password "$name" password
- config_get username "$name" username
-
- if [ -n "$cert_password" ]; then
- create_temp_file /var/run/openvpn.$name.pass
- echo "$cert_password" > /var/run/openvpn.$name.pass
- ret=" --askpass /var/run/openvpn.$name.pass "
- fi
-
- if [ -n "$username" ]; then
- create_temp_file /var/run/openvpn.$name.userpass
- echo "$username" > /var/run/openvpn.$name.userpass
- echo "$password" >> /var/run/openvpn.$name.userpass
- ret=" --auth-user-pass /var/run/openvpn.$name.userpass "
- fi
-
- # Return overrides
- echo "$ret"
-}
-
-openvpn_add_instance() {
- local name="$1"
- local dir="$2"
- local conf=$(basename "$3")
- local security="$4"
- local up="$5"
- local down="$6"
- local route_up="$7"
- local route_pre_down="$8"
- local ipchange="$9"
- local client=$(grep -qEx "client|tls-client" "$dir/$conf" && echo 1)
-
- procd_open_instance "$name"
- procd_set_param command "$PROG" \
- --syslog "openvpn($name)" \
- --status "/var/run/openvpn.$name.status" \
- --cd "$dir" \
- --config "$conf"
- # external scripts can only be called on script-security 2 or higher
- if [ "${security:-2}" -lt 2 ]; then
- logger -t "openvpn(${name})" "not adding hotplug scripts due to script-security ${security:-2}"
- else
- procd_append_param command \
- --up "/usr/libexec/openvpn-hotplug up $name" \
- --down "/usr/libexec/openvpn-hotplug down $name" \
- --route-up "/usr/libexec/openvpn-hotplug route-up $name" \
- --route-pre-down "/usr/libexec/openvpn-hotplug route-pre-down $name" \
- ${client:+--ipchange "/usr/libexec/openvpn-hotplug ipchange $name"} \
- ${up:+--setenv user_up "$up"} \
- ${down:+--setenv user_down "$down"} \
- ${route_up:+--setenv user_route_up "$route_up"} \
- ${route_pre_down:+--setenv user_route_pre_down "$route_pre_down"} \
- ${client:+${ipchange:+--setenv user_ipchange "$ipchange"}}
- fi
- procd_append_param command \
- --script-security "${security:-2}" \
- $(openvpn_get_dev "$name" "$conf") \
- $(openvpn_get_credentials "$name" "$conf")
- procd_set_param file "$dir/$conf"
- procd_set_param term_timeout 15
- procd_set_param respawn
- procd_append_param respawn 3600
- procd_append_param respawn 5
- procd_append_param respawn -1
- procd_close_instance
-}
-
-start_uci_instance() {
- local s="$1"
-
- config_get config "$s" config
- config="${config:+$(readlink -f "$config")}"
-
- section_enabled "$s" || {
- append UCI_DISABLED "$config" "$LIST_SEP"
- return 1
- }
-
- local up down route_up route_pre_down ipchange script_security
- config_get up "$s" up
- config_get down "$s" down
- config_get route_up "$s" route_up
- config_get route_pre_down "$s" route_pre_down
- config_get ipchange "$s" ipchange
- config_get script_security "$s" script_security
-
- [ ! -d "/var/run" ] && mkdir -p "/var/run"
-
- if [ ! -z "$config" ]; then
- append UCI_STARTED "$config" "$LIST_SEP"
- [ -n "$script_security" ] || get_openvpn_option "$config" script_security script-security
- [ -n "$up" ] || get_openvpn_option "$config" up up
- [ -n "$down" ] || get_openvpn_option "$config" down down
- [ -n "$route_up" ] || get_openvpn_option "$config" route_up route-up
- [ -n "$route_pre_down" ] || get_openvpn_option "$config" route_pre_down route-pre-down
- [ -n "$ipchange" ] || get_openvpn_option "$config" ipchange ipchange
- openvpn_add_instance "$s" "${config%/*}" "$config" "$script_security" "$up" "$down" "$route_up" "$route_pre_down" "$ipchange"
- return
- fi
-
- create_temp_file "/var/etc/openvpn-$s.conf"
-
- append_bools "$s" $OPENVPN_BOOLS
- append_params "$s" $OPENVPN_PARAMS
- append_list "$s" $OPENVPN_LIST
-
- openvpn_add_instance "$s" "/var/etc" "openvpn-$s.conf" "$script_security" "$up" "$down" "$route_up" "$route_pre_down" "$ipchange"
-}
-
-start_path_instances() {
- local path name
-
- for path in ${PATH_INSTANCE_DIR}/*.conf; do
- [ -f "$path" ] && {
- name="${path##*/}"
- name="${name%.conf}"
- start_path_instance "$name"
- }
- done
-}
-
-start_path_instance() {
- local name="$1"
-
- local path name up down route_up route_pre_down ipchange
-
- path="${PATH_INSTANCE_DIR}/${name}.conf"
-
- # don't start configs again that are already started by uci
- if echo "$UCI_STARTED" | grep -qxF "$path"; then
- logger -t openvpn "$name.conf already started"
- return
- fi
-
- # don't start configs which are set to disabled in uci
- if echo "$UCI_DISABLED" | grep -qxF "$path"; then
- logger -t openvpn "$name.conf is disabled in /etc/config/openvpn"
- return
- fi
-
- get_openvpn_option "$path" up up || up=""
- get_openvpn_option "$path" down down || down=""
- get_openvpn_option "$path" route_up route-up || route_up=""
- get_openvpn_option "$path" route_pre_down route-pre-down || route_pre_down=""
- get_openvpn_option "$path" ipchange ipchange || ipchange=""
-
- openvpn_add_instance "$name" "${path%/*}" "$path" "" "$up" "$down" "$route_up" "$route_pre_down" "$ipchange"
-}
-
-start_service() {
- local instance="$1"
- local instance_found=0
-
- config_cb() {
- local type="$1"
- local name="$2"
- if [ "$type" = "openvpn" ]; then
- if [ -n "$instance" -a "$instance" = "$name" ]; then
- instance_found=1
- fi
- fi
- }
-
- . /lib/functions/openvpn.sh
- . /usr/share/openvpn/openvpn.options
- config_load 'openvpn'
-
- if [ -n "$instance" ]; then
- if [ "$instance_found" -gt 0 ]; then
- start_uci_instance "$instance"
- elif [ -f "${PATH_INSTANCE_DIR}/${instance}.conf" ]; then
- start_path_instance "$instance"
- fi
- else
- config_foreach start_uci_instance 'openvpn'
-
- auto="$(uci_get openvpn globals autostart 1)"
- if [ "$auto" = "1" ]; then
- start_path_instances
- else
- logger -t openvpn "Autostart for configs in '$PATH_INSTANCE_DIR/*.conf' disabled"
- fi
- fi
-}
-
-service_triggers() {
- procd_add_reload_trigger openvpn
-}
-OPENVPN_PARAMS='
-allow_compression
-askpass
+# proto ==> ovpnproto
+# options suffixed with :d are deprecated
+
+OPENVPN_PARAMS_STRING='
+allow_compression:d
auth
auth_gen_token
auth_gen_token_secret
auth_retry
-auth_user_pass
auth_user_pass_verify
-bcast_buffers
bind_dev
-ca
capath
-cd
-cert
chroot
cipher
client_config_dir
client_crresponse
client_disconnect
client_nat
-comp_lzo
-compat_mode
-compress
+comp_lzo:d
+compress:d
connect_freq
connect_freq_initial
-connect_retry
-connect_retry_max
-connect_timeout
crl_verify
data_ciphers_fallback
dev
dev_node
dev_type
-dh
dhcp_option
dns
down
ecdh_curve
echo
engine
-explicit_exit_notify
-extra_certs
fragment
group
-hand_window
hash_size
http_proxy
http_proxy_option
ifconfig_pool
ifconfig_pool_persist
ifconfig_push
-ignore_unknown_option
inactive
ipchange
iproute
iroute
iroute_ipv6
keepalive
-key
-key_direction
keying_material_exporter
learn_address
-link_mtu
lladdr
local
log
log_append
-lport
management
management_client_group
management_client_user
management_external_cert
management_external_key
-management_log_cache
mark
-max_clients
-max_packet_size
-max_routes_per_client
mode
-mssfix
mtu_disc
-mute
-nice
+ovpnproto
peer_fingerprint
-ping
-ping_exit
-ping_restart
-pkcs11_cert_private
pkcs11_id
-pkcs11_pin_cache
-pkcs11_private_mode
-pkcs11_protected_authentication
pkcs11_providers
-pkcs12
plugin
-port
port_share
-proto
proto_force
providers
pull_filter
push
push_remove
-rcvbuf
redirect_gateway
redirect_private
remap_usr1
-remote
remote_cert_eku
remote_cert_ku
remote_cert_tls
-reneg_bytes
-reneg_pkts
-reneg_sec
replay_persist
replay_window
resolv_retry
route_gateway
route_ipv6
route_ipv6_gateway
-route_metric
route_pre_down
route_up
-rport
-script_security
-secret
server
server_bridge
server_ipv6
-server_poll_timeout
-session_timeout
setcon
-setenv
-setenv_safe
-shaper
-sndbuf
socket_flags
socks_proxy
stale_routes_check
static_challenge
-status
-status_version
-syslog
-tcp_queue_limit
tls_auth
tls_cert_profile
-tls_crypt
-tls_crypt_v2
tls_crypt_v2_verify
tls_export_cert
-tls_timeout
tls_verify
-tls_version_min
tls_version_max
+tls_version_min
tmp_dir
topology
-tran_window
-tun_max_mtu
-tun_mtu
-tun_mtu_extra
-txqueuelen
up
user
-verb
verify_client_cert
-verify_hash
+verify_hash:d
verify_x509_name
vlan_accept
-vlan_pvid
-writepid
x509_track
x509_username_field
'
+OPENVPN_PARAMS_DEPRECATED='
+allow_compression
+comp_lzo
+comp_noadapt
+compress
+link_mtu
+opt_verify
+secret
+verify_hash
+'
+
+OPENVPN_PARAMS_REMOVED='
+ncp_disable
+ncp_ciphers
+'
+
+# peer_fingerprint and verify_hash can be either file or string
+OPENVPN_PARAMS_FILE='
+askpass
+auth_user_pass
+ca
+cert
+config
+dh
+extra_certs
+extra_certs
+http_proxy_user_pass
+key
+pkcs12
+secret:d
+tls_crypt
+tls_crypt_v2
+'
+
+OPENVPN_INTS='
+nice
+'
+
+OPENVPN_UINTS='
+auth_gen_token_lifetime
+bcast_buffers
+connect_retry
+connect_retry_max
+connect_timeout
+explicit_exit_notify
+hand_window
+key_direction
+link_mtu:d
+lport
+management_log_cache
+max_clients
+max_packet_size
+max_routes_per_client
+mssfix
+mute
+ping
+ping_exit
+ping_restart
+pkcs11_cert_private
+pkcs11_pin_cache
+pkcs11_private_mode
+pkcs11_protected_authentication
+port
+rcvbuf
+reneg_bytes
+reneg_pkts
+reneg_sec
+route_metric
+rport
+script_security
+server_poll_timeout
+session_timeout
+shaper
+sndbuf
+socks_proxy_retry
+status_version
+tcp_queue_limit
+tls_timeout
+tran_window
+tun_max_mtu
+tun_mtu
+tun_mtu_extra
+txqueuelen
+up_delay
+verb
+vlan_pvid
+'
+
OPENVPN_BOOLS='
allow_pull_fqdn
allow_recursive_routing
ccd_exclusive
client
client_to_client
-comp_noadapt
-disable
+comp_noadapt:d
disable_dco
disable_occ
down_pre
multihome
mute_replay_warnings
nobind
-opt_verify
+opt_verify:d
passtos
persist_key
persist_local_ip
tls_client
tls_exit
tls_server
-up_delay
up_restart
use_prediction_resistance
username_as_common_name
OPENVPN_LIST='
data_ciphers
-ncp_ciphers
+disable
+ignore_unknown_option
+push
+remote
+route
+setenv
+setenv_safe
tls_cipher
tls_ciphersuites
tls_groups
--- /dev/null
+#!/usr/bin/env ucode
+
+// 0 == link_mtu => historic
+// cmd tun_dev tun_mtu 0 ifconfig_local_ip ifconfig_remote_ip [init | restart]
+// cmd tap_dev tap_mtu 0 ifconfig_local_ip ifconfig_netmask [init | restart]
+
+print('tun_dev ', ARGV[0],' tun_mtu ', ARGV[1], ' ', ARGV[2], ' if_lip "', ARGV[3], '" if_rip "', ARGV[4], '" i/r "', ARGV[5], '"\n');
+print('tap_dev ', ARGV[0],' tap_mtu ', ARGV[1], ' ', ARGV[2], ' if_lip "', ARGV[3], '" if_nm "', ARGV[4], '" i/r "', ARGV[5], '"\n');
+
+/* e.g.
+tun_dev asdf0 tun_mtu 1500 0 if_lip "" if_rip "" i/r "init"
+*/
+
+// See the environment variables passed to this script: https://ucode.mein.io/module-core.html#getenv
+print(getenv(), '\n');
+
+// https://openvpn.net/community-docs/community-articles/openvpn-2-6-manual.html#environmental-variables-177179
+// https://openvpn.net/community-docs/community-articles/openvpn-2-6-manual.html#script-hooks-177179
+
+/* e.g.
+{
+ "script_type": "route-pre-down",
+ "dev": "asdf0",
+ "tun_mtu": "1500",
+ "script_context": "init",
+ "signal": "sigint",
+ "redirect_gateway": "0",
+ "dev_type": "tun",
+ "verb": "6",
+ "daemon": "0",
+ "daemon_log_redirect": "0",
+ "daemon_start_time": "1770390291",
+ "daemon_pid": "7435",
+ "proto_1": "udp",
+ "local_port_1": "1194",
+ "remote_1": "192.0.2.10",
+ "remote_port_1": "1194"
+}
+*/
--- /dev/null
+#!/usr/bin/env ucode
+
+// See the environment variables passed to this script: https://ucode.mein.io/module-core.html#getenv
+print(getenv(), '\n');
+
+// https://openvpn.net/community-docs/community-articles/openvpn-2-6-manual.html#environmental-variables-177179
+// https://openvpn.net/community-docs/community-articles/openvpn-2-6-manual.html#script-hooks-177179
+
+/* e.g.
+{
+ "script_type": "route-up",
+ "dev": "asdf0",
+ "tun_mtu": "1500",
+ "script_context": "init",
+ "signal": "sigint",
+ "redirect_gateway": "0",
+ "dev_type": "tun",
+ "verb": "6",
+ "daemon": "0",
+ "daemon_log_redirect": "0",
+ "daemon_start_time": "1770390291",
+ "daemon_pid": "7435",
+ "proto_1": "udp",
+ "local_port_1": "1194",
+ "remote_1": "192.0.2.10",
+ "remote_port_1": "1194"
+}
+*/
--- /dev/null
+#!/usr/bin/env ucode
+
+// cmd certificate_depth subject
+
+print('cmd ', ARGV, '\n');
+
+// See the environment variables passed to this script: https://ucode.mein.io/module-core.html#getenv
+print(getenv(), '\n');
+
+// https://openvpn.net/community-docs/community-articles/openvpn-2-6-manual.html#script-hooks-177179
+// https://openvpn.net/community-docs/community-articles/openvpn-2-6-manual.html#environmental-variables-177179
+
+
+/* do something */
+
+
+/*
+exit(0); // allow the TLS handshake to proceed
+exit(1); // fail
+*/
--- /dev/null
+#!/usr/bin/env ucode
+
+// 0 == link_mtu => historic
+// cmd tun_dev tun_mtu 0 ifconfig_local_ip ifconfig_remote_ip [init | restart]
+// cmd tap_dev tap_mtu 0 ifconfig_local_ip ifconfig_netmask [init | restart]
+
+print('tun_dev ', ARGV[0],' tun_mtu ', ARGV[1], ' ', ARGV[2], ' if_lip "', ARGV[3], '" if_rip "', ARGV[4], '" i/r "', ARGV[5], '"\n');
+print('tap_dev ', ARGV[0],' tap_mtu ', ARGV[1], ' ', ARGV[2], ' if_lip "', ARGV[3], '" if_nm "', ARGV[4], '" i/r "', ARGV[5], '"\n');
+
+/* e.g.
+tun_dev asdf0 tun_mtu 1500 0 if_lip "" if_rip "" i/r "init"
+*/
+
+// See the environment variables passed to this script: https://ucode.mein.io/module-core.html#getenv
+print(getenv(), '\n');
+
+// https://openvpn.net/community-docs/community-articles/openvpn-2-6-manual.html#environmental-variables-177179
+// https://openvpn.net/community-docs/community-articles/openvpn-2-6-manual.html#script-hooks-177179
+
+/* e.g.
+{
+ "script_type": "up",
+ "dev_type": "tun",
+ "dev": "asdf0",
+ "tun_mtu": "1500",
+ "script_context": "init",
+ "verb": "6",
+ "daemon": "0",
+ "daemon_log_redirect": "0",
+ "daemon_start_time": "1770389649",
+ "daemon_pid": "6116",
+ "proto_1": "udp",
+ "local_port_1": "1194",
+ "remote_1": "192.0.2.10",
+ "remote_port_1": "1194"
+}
+*/
+++ /dev/null
-#!/bin/sh
-
-ACTION=$1
-shift
-INSTANCE=$1
-shift
-
-export ACTION=$ACTION
-export INSTANCE=$INSTANCE
-exec /sbin/hotplug-call openvpn "$@"