diff --git a/.gitmodules b/.gitmodules
index 1ca44949a..a4b1b024a 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -13,3 +13,6 @@
[submodule "vehicle/OVMS.V3/components/wolfssl/wolfssl"]
path = vehicle/OVMS.V3/components/wolfssl/wolfssl
url = https://github.com/wolfSSL/wolfssl.git
+[submodule "vehicle/OVMS.V3/components/esp_wireguard"]
+ path = vehicle/OVMS.V3/components/esp_wireguard
+ url = https://github.com/trombik/esp_wireguard.git
diff --git a/docs/source/userguide/index.rst b/docs/source/userguide/index.rst
index 32e341240..f2cfd812e 100644
--- a/docs/source/userguide/index.rst
+++ b/docs/source/userguide/index.rst
@@ -14,6 +14,7 @@ User Guide
logging
configuration
wifi
+ vpn
vfs
metrics
ota
diff --git a/docs/source/userguide/vpn-ovms-wireguard.png b/docs/source/userguide/vpn-ovms-wireguard.png
new file mode 100644
index 000000000..2fb207a63
Binary files /dev/null and b/docs/source/userguide/vpn-ovms-wireguard.png differ
diff --git a/docs/source/userguide/vpn.rst b/docs/source/userguide/vpn.rst
new file mode 100644
index 000000000..93243c745
--- /dev/null
+++ b/docs/source/userguide/vpn.rst
@@ -0,0 +1,273 @@
+.. highlight:: none
+
+=============================
+Virtual Private Network (VPN)
+=============================
+
+.. warning:: **WireGuard VPN support is only available on (upcoming) ESP-IDF v5+ builds** - unfortunately there is no
+ easy way to make it work with (current) ESP-IDF v3.3.4 builds.
+
+The OVMS module can be (optionally) configured with a `WireGuard `_ VPN tunnel.
+This allows your module to connect (attach) to a private network as if it were part of this network, like
+a local node.
+In addition WireGuard VPN natively supports roaming, thus if your module changes network, or source IP address
+for whatever reason, it will still be connected and visible from your private network.
+
+Possible use cases :
+ - securing the (incoming) connection to your module, so that it's not publicly visible (and scannable) on a public IP address, thus reducing the attack surface
+ - have an always-on SSH connexion to your module - even if it's roaming
+ - being able to (securely) send files and data, back and forth, from / to your module to / from your private network
+
+----------------------
+Principle of operation
+----------------------
+
+At the moment, only one connexion is supported. When configured the VPN tunnel will connect as soon as the
+network is ready, and your module will be able to communicate with other nodes of your private network.
+
+
+.. note:: **WireGuard VPN needs the time to be monotonic**, i.e. always goes forward. While not mandatory, it's best to have
+ a time/date properly setup (NTP, GPS, ...)
+
+.. note:: You will need a properly configured (and tested) WireGuard peer that we will refer to as **the server** (WireGuard VPN has no
+ such distinction between clients and servers, but in our case it's easier to grasp), while we will consider that our
+ module is **the client**.
+
+The **server** needs to be properly configured to accept incoming WireGuard traffic (i.e. UDP packets) on a public interface - we will
+call the address of this public interface the **Peer Endpoint**.
+
+-------------
+Configuration
+-------------
+
+New configuration items are introduced to configure this VPN tunnel, organised using the :doc:`configuration system`::
+
+ =
+
+Where **parameter** is always ``network.wireguard``, and **instance** is as follow:
+
+
+========================= =====
+Instance (in OVMS) Comment
+========================= =====
+``local_ip_address`` The IP address of your module IN YOUR PRIVATE NETWORK
+``local_ip_netmask`` The netmask of your module IN YOUR PRIVATE NETWORK
+``local_private_key`` The PRIVATE key of your module for your WireGuard VPN tunnel
+``local_port`` The UDP port from which your module will communicate with the WireGuard server
+``peer_endpoint`` The IP address or hostname of the WireGuard server
+``peer_public_key`` The PUBLIC key of the WireGuard server
+``peer_port`` The UDP port on which the WireGuard server listens
+``preshared_key`` A (recommended) pre-shared symmetric key for additional protection
+``persistent_keepalive`` A keep-alive if your module is behind NAT (recommended for NAT)
+========================= =====
+
+Example::
+
+ OVMS# config set network.wireguard local_ip_address 192.168.4.58
+ OVMS# config set network.wireguard local_ip_netmask 255.255.255.0
+ OVMS# config set network.wireguard local_private_key "IsvT72MAXzA8EtV0FSD1QT59B4x0oe6Uea5rd/dDzhE="
+ OVMS# config set network.wireguard local_port 11010
+ OVMS# config set network.wireguard peer_endpoint demo.wireguard.com
+ OVMS# config set network.wireguard peer_public_key "FjrsQ/HD1Q8fUlFILIasDlOuajMeZov4NGqMJpkswiw="
+ OVMS# config set network.wireguard peer_port 12912
+ OVMS# config set network.wireguard preshared_key "0/2H97Sd5EJ9LAAAYUglVjPYv7ihNIm/ziuv6BtSI50="
+ OVMS# config set network.wireguard persistent_keepalive 25
+
+.. note:: The preceding values are for documentation only, they won't work as is. If you need to test the tunnel
+ on a demo server, have a look at `WireGuard Demo `_, download the shell script
+ and adapt it to your needs.
+
+``local_ip_address`` and ``local_ip_netmask`` are used to decide:
+ - what will be the IP address of the module in your private network (``local_ip_address``). A new (local) network interface will be configured with this IP address.
+ - which routing will be allowed through the VPN tunnel (using ``local_ip_address`` + ``local_ip_netmask``)
+
+In general, ``local_ip_netmask`` will be the netmask of your private network to enable packets from/to your other network nodes to cross the tunnel.
+(In opposite, when setting the reciprocate configuration, you would define only the)
+
+Correspondance with WireGuard's `Configuration file format `_
+
+========================= =========================================== ============================================================
+OVMS Wireguard Client Wireguard Server
+========================= =========================================== ============================================================
+``local_ip_address`` Peer / AllowedIPs (one address only) (address of the server in the private network)
+``local_ip_netmask`` Peer / AllowedIPs (one address only) (netmask of the private network)
+``local_private_key`` Interface / PrivateKey Peer / PublicKey
+``local_port`` Interface / ListenPort N/A
+``peer_endpoint`` Peer / Endpoint (before colon) (public IP address of the server)
+``peer_public_key`` Peer / PublicKey Interface / PrivateKey
+``peer_port`` Peer / Endpoint (port number, after colon) Interface / ListenPort
+``preshared_key`` Peer / PresharedKey Peer / PresharedKey
+``persistent_keepalive`` Peer / PersistentKeepalive Peer / PersistentKeepalive
+========================= =========================================== ============================================================
+
+^^^^^^^^^^^
+Quick start
+^^^^^^^^^^^
+.. note:: for configuring the different keys (private keys, publics keys, shared key) you will need to access a computer with the proper WireGuard
+ VPN tools installed. In this documentation we will assume that the command line tool ``wg`` is installed on this computer.
+
+The example configuration is the following:
+
+.. image:: vpn-ovms-wireguard.png
+
+* The server listens on ``wg.example.net``, on UDP port ``51820``
+* The private network is ``172.16.0.0/12`` (corresponding netmask ``255.240.0.0``)
+* In this private network:
+
+ * the OVMS module has a reserved IP of ``172.16.10.20``
+ * the server has a reserved IP of ``172.16.1.10`` (for information purposes, not used in the configuration)
+* The OVMS module uses UDP port ``11010`` for its WireGuard VPN purposes.
+
+Steps::
+
+ # Generate the server private key
+ $ wg genkey
+ SGfeKo9cmhIJ5tpDOjOimnEKi3M+3mJ21+jJ7otllHI=
+
+ # Generate the corresponding server public key
+ $ echo 'SGfeKo9cmhIJ5tpDOjOimnEKi3M+3mJ21+jJ7otllHI=' | wg pubkey
+ r5xRjMaiu1apmP6YzRviL8djtBfjcWGnOd2mmHNOqm4=
+
+ # Generate the ovms module private key
+ $ wg genkey
+ kI2/adVWRseT2ZtYxn/5lTzQsPKK8F7YHWcIo3iwgHk=
+
+ # Generate the corresponding ovms module public key
+ $ echo 'kI2/adVWRseT2ZtYxn/5lTzQsPKK8F7YHWcIo3iwgHk=' | wg pubkey
+ hi0SNx8JJVLWIOuIfeQW5Ea5SK/41g4DeXJ2eJR9R3Y=
+
+ # Generate the pre-shared key
+ $ wg genpsk
+ JUvxtI5sFm9n0zY6N4Z8rz/nSnww2DaeFKsOPGnC1WA=
+
+
+Server configuration::
+
+ [Interface]
+ PrivateKey = SGfeKo9cmhIJ5tpDOjOimnEKi3M+3mJ21+jJ7otllHI=
+ ListenPort = 51820
+
+ [Peer]
+ PublicKey = hi0SNx8JJVLWIOuIfeQW5Ea5SK/41g4DeXJ2eJR9R3Y=
+ PresharedKey = JUvxtI5sFm9n0zY6N4Z8rz/nSnww2DaeFKsOPGnC1WA=
+ AllowedIPs = 172.16.10.20/32
+ PersistentKeepalive = 25
+
+OVMS configuration::
+
+ OVMS# config set network.wireguard local_ip_address 172.16.10.20
+ OVMS# config set network.wireguard local_ip_netmask 255.240.0.0
+ OVMS# config set network.wireguard local_port 11010
+ OVMS# config set network.wireguard local_private_key "kI2/adVWRseT2ZtYxn/5lTzQsPKK8F7YHWcIo3iwgHk="
+ OVMS# config set network.wireguard peer_endpoint wg.example.net
+ OVMS# config set network.wireguard peer_port 51820
+ OVMS# config set network.wireguard peer_public_key "r5xRjMaiu1apmP6YzRviL8djtBfjcWGnOd2mmHNOqm4="
+ OVMS# config set network.wireguard preshared_key "JUvxtI5sFm9n0zY6N4Z8rz/nSnww2DaeFKsOPGnC1WA="
+ OVMS# config set network.wireguard persistent_keepalive 25
+
+.. note:: The preceding values are for documentation only, they won't work as is.
+
+^^^^^^^^^^^^^^^
+Troubleshooting
+^^^^^^^^^^^^^^^
+
+If the module doesn't find your WireGuard server, you can use the following tools for diagnostic::
+
+ OVMS# wireguard status
+
+ OVMS# network status
+
+ OVMS# wireguard stop
+ OVMS# wireguard start
+ OVMS# wireguard restart
+
+ OVMS# network ping X.Y.Z.T
+
+Set the logs to debug, and every 10s the wireguard subsystem will print if the peer is 'up' or 'down'.
+
+When neither the WireGuard VPN nor the WiFi client are active, the output of ``network status`` looks like this::
+
+ OVMS# network status
+ Interface#2: ap2 (ifup=1 linkup=1)
+ IPv4: 192.168.4.1/255.255.255.0 gateway 192.168.4.1
+
+ Interface#1: st1 (ifup=0 linkup=0)
+ IPv4: 0.0.0.0/0.0.0.0 gateway 0.0.0.0
+
+ DNS: None
+
+ Default Interface: ap2 (192.168.4.1/255.255.255.0 gateway 192.168.4.1)
+
+As soon as the WiFi client is connected, the WireGuard VPN interface appears, but not connected (``linkup=0``)::
+
+ OVMS# network status
+ Interface#3: wg3 (ifup=1 linkup=0)
+ IPv4: 172.16.10.20/255.255.255.0 gateway 0.0.0.0
+
+ Interface#2: ap2 (ifup=1 linkup=1)
+ IPv4: 192.168.4.1/255.255.255.0 gateway 192.168.4.1
+
+ Interface#1: st1 (ifup=1 linkup=1)
+ IPv4: 192.168.1.12/255.255.255.0 gateway 192.168.1.1
+
+ DNS: 192.168.1.1
+
+ Default Interface: st1 (192.168.1.12/255.255.255.0 gateway 192.168.1.1)
+
+The status is::
+
+ OVMS# wireguard status
+ Connection status: started
+ Peer status: down
+
+After a little while, the WireGuard VPN interface connects (``linkup=1``) and the status is ``up``::
+
+ OVMS# network status
+ Interface#3: wg3 (ifup=1 linkup=1)
+ IPv4: 172.16.10.20/255.255.255.0 gateway 0.0.0.0
+
+ Interface#2: ap2 (ifup=1 linkup=1)
+ IPv4: 192.168.4.1/255.255.255.0 gateway 192.168.4.1
+
+ Interface#1: st1 (ifup=1 linkup=1)
+ IPv4: 192.168.1.12/255.255.255.0 gateway 192.168.1.1
+
+ DNS: 192.168.1.1
+
+ Default Interface: st1 (192.168.1.12/255.255.255.0 gateway 192.168.1.1)
+
+ OVMS# wireguard status
+ Connection status: started
+ Peer status: up
+
+And you can ping another host on the private network::
+
+ OVMS# network ping 172.16.1.2
+ PING 172.16.1.2 (172.16.1.2): 64 data bytes
+ 64 bytes from 172.16.1.2: icmp_seq=0 ttl=64 time=2 ms
+ 64 bytes from 172.16.1.2: icmp_seq=1 ttl=64 time=2 ms
+ 64 bytes from 172.16.1.2: icmp_seq=2 ttl=64 time=5 ms
+ 64 bytes from 172.16.1.2: icmp_seq=3 ttl=64 time=3 ms
+
+ --- 172.16.1.2 ping statistics ---
+ 4 packets transmitted, 4 received, 0% packet loss, time 12ms
+ round-trip avg = 3.00 ms
+
+-----------------------
+Firewall considerations
+-----------------------
+
+WireGuard is an UDP-based protocol.
+There is one UDP port to consider for each node (one for the server, one for the module).
+By convention, the UDP port is often **51820** but can be changed with no issue.
+
+For the **server**, the firewall needs to accept incoming UDP packets on a public interface from the possible source addresses of your module. (In case of a WiFi network you manage, this can be a certain range. However if your module is either roaming or using a mobile network with unknown source
+addresses you may have to open this UDP port to the whole internet).
+
+------------------------
+Bandwidth considerations
+------------------------
+If you use the ``persistent_keepalive`` setting, it will send an UDP packet each ``persistent_keepalive`` seconds.
+The recommended value for systems behind NATs is 25s, so it can make your module send a lot of (small) packets many time per hour.
+
+If this is a concern you may want to disable the tunnel when you don't need it (``wireguard stop``) and only enable it when needed (``wireguard start``)
diff --git a/vehicle/OVMS.V3/Makefile b/vehicle/OVMS.V3/Makefile
index 8d6744e55..dd0628307 100644
--- a/vehicle/OVMS.V3/Makefile
+++ b/vehicle/OVMS.V3/Makefile
@@ -5,4 +5,7 @@
PROJECT_NAME := ovms3
+# Not compatible with ESP-IDF < 4
+EXCLUDE_COMPONENTS := esp_wireguard
+
include $(IDF_PATH)/make/project.mk
diff --git a/vehicle/OVMS.V3/changes.txt b/vehicle/OVMS.V3/changes.txt
index 55c16e981..34534ddd1 100644
--- a/vehicle/OVMS.V3/changes.txt
+++ b/vehicle/OVMS.V3/changes.txt
@@ -98,6 +98,22 @@ Open Vehicle Monitor System v3 - Change log
obd2ecu.stop -- Called before the OBD2ECU process is stopped.
- Web UI: Add configuration for Valet and Flatbed geofence to the Locations config page.
- Network: New 'network ping' command to ping (ICMP) hostname or IP address. (ESP-IDFv4+ only / needs to be enabled in menuconfig - Developer Options)
+- Network: Add support for WireGuard VPN (ESP-IDFv4+ only / needs to be enabled in menuconfig - Developer Options)
+ New commands:
+ wireguard start -- start the VPN tunnel
+ wireguard stop -- stop the VPN tunnel (and reset default route)
+ wireguard restart -- restart the VPN tunnel (and reset default route)
+ wireguard default -- set default route to use the VPN tunnel
+ New Configs:
+ [network.wireguard] local_ip_address -- The IP address of your module IN YOUR PRIVATE NETWORK
+ [network.wireguard] local_ip_netmask -- The netmask of your module IN YOUR PRIVATE NETWORK
+ [network.wireguard] local_private_key -- The PRIVATE key of your module for your WireGuard VPN tunnel
+ [network.wireguard] local_port -- The UDP port from which your module will communicate with the WireGuard server
+ [network.wireguard] peer_endpoint -- The IP address or hostname of the WireGuard server
+ [network.wireguard] peer_public_key -- The PUBLIC key of the WireGuard server
+ [network.wireguard] peer_port -- The UDP port on which the WireGuard server listens
+ [network.wireguard] preshared_key -- A (recommended) pre-shared symmetric key for additional protection
+ [network.wireguard] persistent_keepalive -- A keep-alive if your module is behind NAT (recommended for NAT)
2022-09-01 MWJ 3.3.003 OTA release
- Toyota RAV4 EV: Initial support added. Only the Tesla bus is decoded and just listening so far.
diff --git a/vehicle/OVMS.V3/components/esp_wireguard b/vehicle/OVMS.V3/components/esp_wireguard
new file mode 160000
index 000000000..a441e52b5
--- /dev/null
+++ b/vehicle/OVMS.V3/components/esp_wireguard
@@ -0,0 +1 @@
+Subproject commit a441e52b5a117f3dc3caa01376726d9dc9331192
diff --git a/vehicle/OVMS.V3/components/ovms_wireguard/CMakeLists.txt b/vehicle/OVMS.V3/components/ovms_wireguard/CMakeLists.txt
new file mode 100644
index 000000000..04a680482
--- /dev/null
+++ b/vehicle/OVMS.V3/components/ovms_wireguard/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(srcs)
+set(include_dirs)
+
+if (CONFIG_OVMS_COMP_WIREGUARD)
+ list(APPEND srcs "src/ovms_wireguard.cpp")
+ list(APPEND include_dirs "src")
+endif ()
+
+# requirements can't depend on config
+idf_component_register(SRCS ${srcs}
+ INCLUDE_DIRS ${include_dirs}
+ PRIV_REQUIRES "main" "esp_wireguard"
+ WHOLE_ARCHIVE)
diff --git a/vehicle/OVMS.V3/components/ovms_wireguard/src/ovms_wireguard.cpp b/vehicle/OVMS.V3/components/ovms_wireguard/src/ovms_wireguard.cpp
new file mode 100644
index 000000000..d1deaf5f9
--- /dev/null
+++ b/vehicle/OVMS.V3/components/ovms_wireguard/src/ovms_wireguard.cpp
@@ -0,0 +1,311 @@
+/*
+; Project: Open Vehicle Monitor System
+; Subject: Support for Wireguard VPN protocol (client)
+; (c) Ludovic LANGE
+;
+; 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.
+*/
+
+#include "ovms_log.h"
+static const char *TAG = "wireguard";
+#include "ovms_command.h"
+#include "ovms_config.h"
+#include "ovms_events.h"
+#include "ovms_wireguard.h"
+
+#define MY_WIREGUARD_CLIENT_INIT_PRIORITY 8900
+
+OvmsWireguardClient MyWireguardClient
+ __attribute__((init_priority(MY_WIREGUARD_CLIENT_INIT_PRIORITY)));
+
+static wireguard_config_t wg_config = ESP_WIREGUARD_CONFIG_DEFAULT();
+
+#define WG_PARAM_NAME "network.wireguard"
+
+// Private key of the WireGuard device
+#define WG_PRIVATE_KEY "local_private_key"
+// Local IP address of the WireGuard device.
+#define WG_LOCAL_IP_ADDRESS "local_ip_address"
+// Netmask of the local network the WireGuard device belongs to.
+#define WG_LOCAL_IP_NETMASK "local_ip_netmask"
+// Local port to listen.
+#define WG_LOCAL_PORT "local_port"
+
+// Public key of the remote peer.
+#define WG_PEER_PUBLIC_KEY "peer_public_key"
+// Address of the remote peer.
+#define WG_PEER_ADDRESS "peer_endpoint"
+
+// Port number of the remote peer.
+#define WG_PEER_PORT "peer_port"
+
+// Wireguard pre-shared symmetric key
+#define WG_PRESHARED_KEY "preshared_key"
+
+// A seconds interval, between 1 and 65535 inclusive, of how often to
+// send an authenticated empty packet to the peer for the purpose of
+// keeping a stateful firewall or NAT mapping valid persistently
+#define WG_PERSISTENT_KEEPALIVE "persistent_keepalive"
+
+static esp_err_t wireguard_setup(wireguard_ctx_t *ctx) {
+ esp_err_t err = ESP_FAIL;
+
+ ESP_LOGI(TAG, "Initializing WireGuard.");
+
+ if (wg_config.private_key) {
+ free((void *)wg_config.private_key);
+ }
+ wg_config.private_key =
+ strdup(MyConfig.GetParamValue(WG_PARAM_NAME, WG_PRIVATE_KEY).c_str());
+
+ wg_config.listen_port =
+ MyConfig.GetParamValueInt(WG_PARAM_NAME, WG_LOCAL_PORT);
+
+ if (wg_config.public_key) {
+ free((void *)wg_config.public_key);
+ }
+ wg_config.public_key =
+ strdup(MyConfig.GetParamValue(WG_PARAM_NAME, WG_PEER_PUBLIC_KEY).c_str());
+
+ if (wg_config.preshared_key) {
+ free((void *)wg_config.preshared_key);
+ }
+ std::string ids =
+ MyConfig.GetParamValue(WG_PARAM_NAME, WG_PRESHARED_KEY).c_str();
+ if (ids != "") {
+ wg_config.preshared_key = strdup(ids.c_str());
+ } else {
+ wg_config.preshared_key = NULL;
+ }
+
+ if (wg_config.allowed_ip) {
+ free((void *)wg_config.allowed_ip);
+ }
+ wg_config.allowed_ip = strdup(
+ MyConfig.GetParamValue(WG_PARAM_NAME, WG_LOCAL_IP_ADDRESS).c_str());
+
+ if (wg_config.allowed_ip_mask) {
+ free((void *)wg_config.allowed_ip_mask);
+ }
+ wg_config.allowed_ip_mask = strdup(
+ MyConfig.GetParamValue(WG_PARAM_NAME, WG_LOCAL_IP_NETMASK).c_str());
+
+ if (wg_config.endpoint) {
+ free((void *)wg_config.endpoint);
+ }
+ wg_config.endpoint =
+ strdup(MyConfig.GetParamValue(WG_PARAM_NAME, WG_PEER_ADDRESS).c_str());
+
+ wg_config.port =
+ MyConfig.GetParamValueInt(WG_PARAM_NAME, WG_PEER_PORT, 51820);
+ wg_config.persistent_keepalive =
+ MyConfig.GetParamValueInt(WG_PARAM_NAME, WG_PERSISTENT_KEEPALIVE);
+
+ err = esp_wireguard_init(&wg_config, ctx);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "esp_wireguard_init: %s", esp_err_to_name(err));
+ }
+
+ return err;
+}
+
+// COMMANDS
+// --------
+
+void wg_status(int verbosity, OvmsWriter *writer, OvmsCommand *cmd, int argc,
+ const char *const *argv) {
+ OvmsWireguardClient *me = &MyWireguardClient;
+ if (me == NULL) {
+ writer->puts("Error: WireguardClient could not be found");
+ return;
+ }
+
+ writer->printf("Connection status: %s\n",
+ (me->started) ? "started" : "stopped");
+
+ if (me->started) {
+ esp_err_t err;
+ err = esp_wireguardif_peer_is_up(&me->ctx);
+ writer->printf("Peer status: %s\n", (err == ESP_OK) ? "up" : "down");
+ }
+}
+
+void wg_start(int verbosity, OvmsWriter *writer, OvmsCommand *cmd, int argc,
+ const char *const *argv) {
+ writer->puts("Starting WireguardClient...");
+ MyWireguardClient.TryStart();
+}
+
+void wg_stop(int verbosity, OvmsWriter *writer, OvmsCommand *cmd, int argc,
+ const char *const *argv) {
+ writer->puts("Stopping WireguardClient...");
+ MyWireguardClient.TryStop();
+}
+
+void wg_restart(int verbosity, OvmsWriter *writer, OvmsCommand *cmd, int argc,
+ const char *const *argv) {
+ writer->puts("Restarting WireguardClient...");
+ MyWireguardClient.TryStop();
+ MyWireguardClient.TryStart();
+}
+
+void wg_set_default(int verbosity, OvmsWriter *writer, OvmsCommand *cmd,
+ int argc, const char *const *argv) {
+ if (MyWireguardClient.started) {
+ esp_err_t err;
+ err = esp_wireguard_set_default(&MyWireguardClient.ctx);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "esp_wireguard_set_default: %s", esp_err_to_name(err));
+ }
+ }
+}
+
+// EVENTS
+// ------
+
+void OvmsWireguardClient::EventSystemStart(std::string event, void *data) {
+ this->UpdateSetup();
+}
+
+void OvmsWireguardClient::EventConfigChanged(std::string event, void *data) {
+ OvmsConfigParam *p = (OvmsConfigParam *)data;
+
+ if (p->GetName().compare(WG_PARAM_NAME) == 0) {
+ bool was_started = this->started;
+ this->TryStop();
+ esp_err_t err;
+ err = this->UpdateSetup();
+ if ((err == ESP_OK) && (was_started)) {
+ this->TryStart();
+ }
+ }
+}
+
+void OvmsWireguardClient::EventTicker(std::string event, void *data) {
+ if (this->started) {
+ esp_err_t err;
+ err = esp_wireguardif_peer_is_up(&this->ctx);
+ if (err == ESP_OK) {
+ ESP_LOGD(TAG, "Peer is up");
+ } else {
+ ESP_LOGD(TAG, "Peer is down");
+ }
+ }
+}
+
+void OvmsWireguardClient::EventNetUp(std::string event, void *data) {
+ ESP_LOGI(TAG, "Starting Wireguard client");
+ this->TryStart();
+}
+
+void OvmsWireguardClient::EventNetDown(std::string event, void *data) {
+ ESP_LOGI(TAG, "Stopping Wireguard client");
+ this->TryStop();
+}
+
+void OvmsWireguardClient::EventNetReconfigured(std::string event, void *data) {
+ esp_err_t err;
+ ESP_LOGI(TAG, "Network was reconfigured: restarting Wireguard client");
+ this->TryStop();
+ this->TryStart();
+}
+
+// Constructor / destructor
+OvmsWireguardClient::OvmsWireguardClient() {
+ ESP_LOGI(TAG, "Initialising Wireguard Client (" STR(
+ MY_WIREGUARD_CLIENT_INIT_PRIORITY) ")");
+
+ esp_log_level_set("esp_wireguard", ESP_LOG_DEBUG);
+ esp_log_level_set("wireguardif", ESP_LOG_DEBUG);
+ esp_log_level_set("wireguard", ESP_LOG_DEBUG);
+
+ MyConfig.RegisterParam(WG_PARAM_NAME, "Wireguard (VPN) Configuration", true,
+ true);
+
+ OvmsCommand *cmd_wg = MyCommandApp.RegisterCommand(
+ "wireguard", "Wireguard framework", wg_status, "", 0, 0, false);
+ cmd_wg->RegisterCommand("status", "Show wireguard status", wg_status, "", 0,
+ 0, false);
+ cmd_wg->RegisterCommand("start", "Start wireguard connexion", wg_start, "", 0,
+ 0, false);
+ cmd_wg->RegisterCommand("stop", "Stop wireguard connexion", wg_stop, "", 0, 0,
+ false);
+ cmd_wg->RegisterCommand("restart", "Restart wireguard connexion", wg_restart,
+ "", 0, 0, false);
+ cmd_wg->RegisterCommand("default", "Set interface as default route",
+ wg_set_default, "", 0, 0, false);
+
+#undef bind // Kludgy, but works
+ using std::placeholders::_1;
+ using std::placeholders::_2;
+ MyEvents.RegisterEvent(
+ TAG, "system.start",
+ std::bind(&OvmsWireguardClient::EventSystemStart, this, _1, _2));
+ MyEvents.RegisterEvent(
+ TAG, "config.changed",
+ std::bind(&OvmsWireguardClient::EventConfigChanged, this, _1, _2));
+ MyEvents.RegisterEvent(
+ TAG, "ticker.10",
+ std::bind(&OvmsWireguardClient::EventTicker, this, _1, _2));
+ MyEvents.RegisterEvent(
+ TAG, "network.up",
+ std::bind(&OvmsWireguardClient::EventNetUp, this, _1, _2));
+ MyEvents.RegisterEvent(
+ TAG, "network.down",
+ std::bind(&OvmsWireguardClient::EventNetDown, this, _1, _2));
+ MyEvents.RegisterEvent(
+ TAG, "network.reconfigured",
+ std::bind(&OvmsWireguardClient::EventNetReconfigured, this, _1, _2));
+
+ this->started = false;
+}
+
+OvmsWireguardClient::~OvmsWireguardClient() {}
+
+// Internal methods
+esp_err_t OvmsWireguardClient::UpdateSetup() {
+ esp_err_t err;
+ err = wireguard_setup(&this->ctx);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "wireguard_setup: %s", esp_err_to_name(err));
+ }
+ return err;
+}
+
+// Start only if config allows it
+void OvmsWireguardClient::TryStart() {
+ esp_err_t err;
+ if (!(this->started)) {
+ ESP_LOGI(TAG, "Connecting to the peer.");
+ err = esp_wireguard_connect(&this->ctx);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "esp_wireguard_connect: %s", esp_err_to_name(err));
+ } else {
+ this->started = true;
+ }
+ }
+}
+
+// Stop only if already started
+void OvmsWireguardClient::TryStop() {
+ if (this->started) {
+ esp_wireguard_disconnect(&this->ctx);
+ this->started = false;
+ }
+}
diff --git a/vehicle/OVMS.V3/components/ovms_wireguard/src/ovms_wireguard.h b/vehicle/OVMS.V3/components/ovms_wireguard/src/ovms_wireguard.h
new file mode 100644
index 000000000..a02451626
--- /dev/null
+++ b/vehicle/OVMS.V3/components/ovms_wireguard/src/ovms_wireguard.h
@@ -0,0 +1,59 @@
+/*
+; Project: Open Vehicle Monitor System
+; Subject: Support for Wireguard VPN protocol (client)
+; (c) Ludovic LANGE
+;
+; 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.
+*/
+
+#ifndef __OVMS_WIREGUARD_H__
+#define __OVMS_WIREGUARD_H__
+
+#include
+// #include