From 507955757bb75bf8d5bba09fb385bb34d267e675 Mon Sep 17 00:00:00 2001 From: Ludovic LANGE Date: Sun, 23 Apr 2023 21:23:37 +0200 Subject: [PATCH] networking: add support for WireGuard VPN This VPN allows your OVMS module to "connect" itself to your own private network, make it visible and reachable, whatever the connexion it is using (WiFi, Mobile, ...) (Note: 3G untested at the moment) It should also (unverified) be able to roam from one connexion to the other, or even among different WiFi networks, without any specific setting to do. A documentation has been added. (Note: The schematics, drawn with [draw.io](https://get.diagrams.net/) is embedded in the resulting PNG file, so you can open the PNG file with the editor to edit the diagram. Please keep it that way (by checking the 'Include a copy of my diagram' check box)) Support for wireguard on ESP provided by https://github.com/trombik/esp_wireguard , cf https://github.com/trombik/esp_wireguard/blob/main/LICENSE Cf #752 Signed-off-by: Ludovic LANGE --- .gitmodules | 3 + docs/source/userguide/index.rst | 1 + docs/source/userguide/vpn-ovms-wireguard.png | Bin 0 -> 101580 bytes docs/source/userguide/vpn.rst | 273 +++++++++++++++ vehicle/OVMS.V3/Makefile | 3 + vehicle/OVMS.V3/changes.txt | 16 + vehicle/OVMS.V3/components/esp_wireguard | 1 + .../components/ovms_wireguard/CMakeLists.txt | 13 + .../ovms_wireguard/src/ovms_wireguard.cpp | 311 ++++++++++++++++++ .../ovms_wireguard/src/ovms_wireguard.h | 59 ++++ vehicle/OVMS.V3/main/Kconfig | 10 + .../OVMS.V3/support/sdkconfig.defaults.esp5 | 1 + 12 files changed, 691 insertions(+) create mode 100644 docs/source/userguide/vpn-ovms-wireguard.png create mode 100644 docs/source/userguide/vpn.rst create mode 160000 vehicle/OVMS.V3/components/esp_wireguard create mode 100644 vehicle/OVMS.V3/components/ovms_wireguard/CMakeLists.txt create mode 100644 vehicle/OVMS.V3/components/ovms_wireguard/src/ovms_wireguard.cpp create mode 100644 vehicle/OVMS.V3/components/ovms_wireguard/src/ovms_wireguard.h 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 0000000000000000000000000000000000000000..2fb207a63a413d0ace9fbcda4083b229e11dc5ba GIT binary patch literal 101580 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

2Yo9oziAnE$RyR)7CQ*Z2jaoAyet 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≤5dbT9cXK>@4taI|E&hl0 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 cLckM}2saN`Xb^%Z4Dd%`_ 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 +#include +// #include "ovms_utils.h" +#include + +class OvmsWireguardClient { + public: + OvmsWireguardClient(); + ~OvmsWireguardClient(); + + public: + wireguard_ctx_t ctx = {0}; + bool started = false; + + public: + void EventConfigChanged(std::string event, void* data); + void EventSystemStart(std::string event, void* data); + void EventTicker(std::string event, void* data); + void EventNetUp(std::string event, void* data); + void EventNetDown(std::string event, void* data); + void EventNetReconfigured(std::string event, void* data); + + public: + void TryStart(); + void TryStop(); + esp_err_t UpdateSetup(); +}; + +extern OvmsWireguardClient MyWireguardClient; + +#endif // #ifndef __OVMS_WIREGUARD_H__ diff --git a/vehicle/OVMS.V3/main/Kconfig b/vehicle/OVMS.V3/main/Kconfig index cf6c8984c..ea1db149a 100644 --- a/vehicle/OVMS.V3/main/Kconfig +++ b/vehicle/OVMS.V3/main/Kconfig @@ -833,6 +833,16 @@ config OVMS_COMP_PLUGINS Enable to include support for PLUGINS and server based extension repositories. +menuconfig OVMS_COMP_WIREGUARD + bool "Include support for WireGuard VPN (client)" + default n + depends on OVMS + help + The WireGuard VPN client support allows your OVMS to connect, via VPN, + to a server or network of your choosing. + When enabled, all outgoing connexions will use this VPN tunnel ; and all + incoming connexions through the tunnel will be accepted as local. + endmenu # Component Options diff --git a/vehicle/OVMS.V3/support/sdkconfig.defaults.esp5 b/vehicle/OVMS.V3/support/sdkconfig.defaults.esp5 index 268bd90eb..1ebdca030 100644 --- a/vehicle/OVMS.V3/support/sdkconfig.defaults.esp5 +++ b/vehicle/OVMS.V3/support/sdkconfig.defaults.esp5 @@ -6,6 +6,7 @@ CONFIG_ESP32_REV_MIN_3=y CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y CONFIG_MG_ENABLE_SSL=n CONFIG_ESP_TASK_WDT=n +CONFIG_OVMS_COMP_WIREGUARD=y # CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y CONFIG_CAN_SPEED_100KBPS=y