diff --git a/examples/disconnect/disconnect.ino b/examples/disconnect/disconnect.ino new file mode 100644 index 0000000..b94ea06 --- /dev/null +++ b/examples/disconnect/disconnect.ino @@ -0,0 +1,105 @@ +#include +#include +#include + +// WiFi configuration --- UPDATE this configuration for your WiFi AP +char ssid[] = "ssid"; +char password[] = "password"; + +// WireGuard configuration --- UPDATE this configuration from JSON +char private_key[] = "(Private Key) "; // [Interface] PrivateKey +IPAddress local_ip(1,2,3,4); // [Interface] Address +char public_key[] = "(Public Key)"; // [Peer] PublicKey +char endpoint_address[] = "link.arc.soracom.io"; // [Peer] Endpoint +int endpoint_port = 11010; // [Peer] Endpoint + +const char host_inside_vpn[] = "10.0.0.1"; +const char host_outside_vpn[] = "192.168.2.1"; + +static constexpr const uint32_t UPDATE_INTERVAL_MS = 5000; + +static WireGuard wg; + +void setup() +{ + esp_log_level_set("*", ESP_LOG_DEBUG); + + Serial.begin(115200); + Serial.println("Connecting to the AP..."); + WiFi.begin(ssid, password); + while( !WiFi.isConnected() ) { + delay(1000); + } + + Serial.println("Adjusting system time..."); + configTime(9 * 60 * 60, 0, "ntp.jst.mfeed.ad.jp", "ntp.nict.jp", "time.google.com"); + + Serial.println("WiFi Connected."); +} + +void loop() +{ + static uint64_t send_count = 0; + static uint64_t loop_count = 0; + if( loop_count % 5 == 0) { + if( !wg.is_initialized() ) { + Serial.println("Initializing WG interface..."); + if( !wg.begin( + local_ip, + private_key, + endpoint_address, + public_key, + endpoint_port) ) { + Serial.println("Failed to initialize WG interface."); + } + } + else { + Serial.println("Shutting down WG interface..."); + wg.end(); + } + } + loop_count++; + + WiFiUDP client; + const char* host = wg.is_initialized() ? host_inside_vpn : host_outside_vpn; + if( !client.beginPacket(host, 23080) ) { + Serial.println("Failed to begin packet..."); + delay(5000); + return; + } + + uint64_t uptime_msec = millis(); + uint8_t buffer[16]; + buffer[ 0] = (uptime_msec >> 0) & 0xff; + buffer[ 1] = (uptime_msec >> 8) & 0xff; + buffer[ 2] = (uptime_msec >> 16) & 0xff; + buffer[ 3] = (uptime_msec >> 24) & 0xff; + buffer[ 4] = (uptime_msec >> 32) & 0xff; + buffer[ 5] = (uptime_msec >> 40) & 0xff; + buffer[ 6] = (uptime_msec >> 48) & 0xff; + buffer[ 7] = (uptime_msec >> 56) & 0xff; + buffer[ 8] = (send_count >> 0) & 0xff; + buffer[ 9] = (send_count >> 8) & 0xff; + buffer[10] = (send_count >> 16) & 0xff; + buffer[11] = (send_count >> 24) & 0xff; + buffer[12] = (send_count >> 32) & 0xff; + buffer[13] = (send_count >> 40) & 0xff; + buffer[14] = (send_count >> 48) & 0xff; + buffer[15] = (send_count >> 56) & 0xff; + + Serial.printf("Sending uptime %lu [ms], count=%d\r\n", uptime_msec, send_count); + client.write(buffer, sizeof(buffer)); + client.endPacket(); + + send_count++; + + IPAddress result; + if( WiFi.hostByName("www.google.com", result) ) { + Serial.printf("hostByName: %s\r\n", result.toString().c_str()); + } + else { + Serial.printf("hostByName failed\r\n"); + } + + delay(UPDATE_INTERVAL_MS); +} diff --git a/src/WireGuard-ESP32.h b/src/WireGuard-ESP32.h index 520746a..29820a4 100644 --- a/src/WireGuard-ESP32.h +++ b/src/WireGuard-ESP32.h @@ -7,6 +7,10 @@ class WireGuard { +private: + bool _is_initialized = false; public: - void begin(const IPAddress& localIP, const char* privateKey, const char* remotePeerAddress, const char* remotePeerPublicKey, uint16_t remotePeerPort); + bool begin(const IPAddress& localIP, const char* privateKey, const char* remotePeerAddress, const char* remotePeerPublicKey, uint16_t remotePeerPort); + void end(); + bool is_initialized() const { return this->_is_initialized; } }; diff --git a/src/WireGuard.cpp b/src/WireGuard.cpp index 7e864e5..15d5c4e 100644 --- a/src/WireGuard.cpp +++ b/src/WireGuard.cpp @@ -8,13 +8,14 @@ #include "freertos/task.h" #include "freertos/event_groups.h" #include "esp_system.h" -#include "esp_log.h" #include "lwip/err.h" #include "lwip/sys.h" #include "lwip/ip.h" #include "lwip/netdb.h" +#include "esp32-hal-log.h" + extern "C" { #include "wireguardif.h" #include "wireguard-platform.h" @@ -23,11 +24,12 @@ extern "C" { // Wireguard instance static struct netif wg_netif_struct = {0}; static struct netif *wg_netif = NULL; +static struct netif *previous_default_netif = NULL; static uint8_t wireguard_peer_index = WIREGUARDIF_INVALID_INDEX; -#define TAG "WireGuard" +#define TAG "[WireGuard] " -void WireGuard::begin(const IPAddress& localIP, const char* privateKey, const char* remotePeerAddress, const char* remotePeerPublicKey, uint16_t remotePeerPort) { +bool WireGuard::begin(const IPAddress& localIP, const char* privateKey, const char* remotePeerAddress, const char* remotePeerPublicKey, uint16_t remotePeerPort) { struct wireguardif_init_data wg; struct wireguardif_peer peer; ip_addr_t ipaddr = IPADDR4_INIT(static_cast(localIP)); @@ -45,24 +47,10 @@ void WireGuard::begin(const IPAddress& localIP, const char* privateKey, const ch wg.bind_netif = NULL; - // Register the new WireGuard network interface with lwIP - wg_netif = netif_add(&wg_netif_struct, ip_2_ip4(&ipaddr), ip_2_ip4(&netmask), ip_2_ip4(&gateway), &wg, &wireguardif_init, &ip_input); - - // Mark the interface as administratively up, link up flag is set automatically when peer connects - netif_set_up(wg_netif); - // Initialise the first WireGuard peer structure wireguardif_peer_init(&peer); - peer.public_key = remotePeerPublicKey; - peer.preshared_key = NULL; - // Allow all IPs through tunnel - { - ip_addr_t allowed_ip = IPADDR4_INIT_BYTES(0, 0, 0, 0); - peer.allowed_ip = allowed_ip; - ip_addr_t allowed_mask = IPADDR4_INIT_BYTES(0, 0, 0, 0); - peer.allowed_mask = allowed_mask; - } // If we know the endpoint's address can add here + bool success_get_endpoint_ip = false; for(int retry = 0; retry < 5; retry++) { ip_addr_t endpoint_ip = IPADDR4_INIT_BYTES(0, 0, 0, 0); struct addrinfo *res = NULL; @@ -73,19 +61,44 @@ void WireGuard::begin(const IPAddress& localIP, const char* privateKey, const ch vTaskDelay(pdMS_TO_TICKS(2000)); continue; } + success_get_endpoint_ip = true; struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr; inet_addr_to_ip4addr(ip_2_ip4(&endpoint_ip), &addr4); lwip_freeaddrinfo(res); peer.endpoint_ip = endpoint_ip; - ESP_LOGI(TAG, "%s is %3d.%3d.%3d.%3d" + log_i(TAG "%s is %3d.%3d.%3d.%3d" , remotePeerAddress , (endpoint_ip.u_addr.ip4.addr >> 0) & 0xff , (endpoint_ip.u_addr.ip4.addr >> 8) & 0xff , (endpoint_ip.u_addr.ip4.addr >> 16) & 0xff , (endpoint_ip.u_addr.ip4.addr >> 24) & 0xff ); + break; + } + if( !success_get_endpoint_ip ) { + log_e(TAG "failed to get endpoint ip."); + return false; + } + // Register the new WireGuard network interface with lwIP + wg_netif = netif_add(&wg_netif_struct, ip_2_ip4(&ipaddr), ip_2_ip4(&netmask), ip_2_ip4(&gateway), &wg, &wireguardif_init, &ip_input); + if( wg_netif == nullptr ) { + log_e(TAG "failed to initialize WG netif."); + return false; + } + // Mark the interface as administratively up, link up flag is set automatically when peer connects + netif_set_up(wg_netif); + + peer.public_key = remotePeerPublicKey; + peer.preshared_key = NULL; + // Allow all IPs through tunnel + { + ip_addr_t allowed_ip = IPADDR4_INIT_BYTES(0, 0, 0, 0); + peer.allowed_ip = allowed_ip; + ip_addr_t allowed_mask = IPADDR4_INIT_BYTES(0, 0, 0, 0); + peer.allowed_mask = allowed_mask; } + peer.endport_port = remotePeerPort; // Initialize the platform @@ -94,9 +107,34 @@ void WireGuard::begin(const IPAddress& localIP, const char* privateKey, const ch wireguardif_add_peer(wg_netif, &peer, &wireguard_peer_index); if ((wireguard_peer_index != WIREGUARDIF_INVALID_INDEX) && !ip_addr_isany(&peer.endpoint_ip)) { // Start outbound connection to peer - ESP_LOGI(TAG, "connecting wireguard..."); + log_i(TAG "connecting wireguard..."); wireguardif_connect(wg_netif, wireguard_peer_index); + // Save the current default interface for restoring when shutting down the WG interface. + previous_default_netif = netif_default; // Set default interface to WG device. netif_set_default(wg_netif); } + + this->_is_initialized = true; + return true; +} + +void WireGuard::end() { + if( !this->_is_initialized ) return; + + // Restore the default interface. + netif_set_default(previous_default_netif); + previous_default_netif = nullptr; + // Disconnect the WG interface. + wireguardif_disconnect(wg_netif, wireguard_peer_index); + // Remove peer from the WG interface + wireguardif_remove_peer(wg_netif, wireguard_peer_index); + wireguard_peer_index = WIREGUARDIF_INVALID_INDEX; + // Shutdown the wireguard interface. + wireguardif_shutdown(wg_netif); + // Remove the WG interface; + netif_remove(wg_netif); + wg_netif = nullptr; + + this->_is_initialized = false; } \ No newline at end of file diff --git a/src/wireguard-platform.c b/src/wireguard-platform.c index cc0d05e..cd909b3 100644 --- a/src/wireguard-platform.c +++ b/src/wireguard-platform.c @@ -14,6 +14,7 @@ static struct mbedtls_ctr_drbg_context random_context; static struct mbedtls_entropy_context entropy_context; +static bool is_platform_initialized = false; static int entropy_hw_random_source( void *data, unsigned char *output, size_t len, size_t *olen ) { esp_fill_random(output, len); @@ -22,10 +23,14 @@ static int entropy_hw_random_source( void *data, unsigned char *output, size_t l } void wireguard_platform_init() { + if( is_platform_initialized ) return; + mbedtls_entropy_init(&entropy_context); mbedtls_ctr_drbg_init(&random_context); mbedtls_entropy_add_source(&entropy_context, entropy_hw_random_source, NULL, 134, MBEDTLS_ENTROPY_SOURCE_STRONG); mbedtls_ctr_drbg_seed(&random_context, mbedtls_entropy_func, &entropy_context, NULL, 0); + + is_platform_initialized = true; } void wireguard_random_bytes(void *bytes, size_t size) { diff --git a/src/wireguardif.c b/src/wireguardif.c index 91b6717..d64ad85 100644 --- a/src/wireguardif.c +++ b/src/wireguardif.c @@ -50,9 +50,11 @@ #include "esp_log.h" #include "tcpip_adapter.h" +#include "esp32-hal-log.h" + #define WIREGUARDIF_TIMER_MSECS 400 -#define TAG "wireguard" +#define TAG "[WireGuard] " static void update_peer_addr(struct wireguard_peer *peer, const ip_addr_t *addr, u16_t port) { peer->ip = *addr; @@ -131,7 +133,7 @@ static err_t wireguardif_output_to_peer(struct netif *netif, struct pbuf *q, con // The IP packet consists of 16 byte header (struct message_transport_data), data padded upto 16 byte boundary + encrypted auth tag (16 bytes) pbuf = pbuf_alloc(PBUF_TRANSPORT, header_len + padded_len + WIREGUARD_AUTHTAG_LEN, PBUF_RAM); if (pbuf) { - ESP_LOGI(TAG, "preparing transport data..."); + log_v(TAG "preparing transport data..."); // Note: allocating pbuf from RAM above guarantees that the pbuf is in one section and not chained // - i.e payload points to the contiguous memory region memset(pbuf->payload, 0, pbuf->tot_len); @@ -214,7 +216,7 @@ static void wireguardif_process_response_message(struct wireguard_device *device if (wireguard_process_handshake_response(device, peer, response)) { // Packet is good // Update the peer location - ESP_LOGI(TAG, "good handshake from %08x:%d", addr->u_addr.ip4.addr, port); + log_i(TAG "good handshake from %08x:%d", addr->u_addr.ip4.addr, port); update_peer_addr(peer, addr, port); wireguard_start_session(peer, true); @@ -224,7 +226,7 @@ static void wireguardif_process_response_message(struct wireguard_device *device netif_set_link_up(device->netif); } else { // Packet bad - ESP_LOGI(TAG, "bad handshake from %08x:%d", addr->u_addr.ip4.addr, port); + log_i(TAG "bad handshake from %08x:%d", addr->u_addr.ip4.addr, port); } } @@ -559,7 +561,7 @@ void wireguardif_network_rx(void *arg, struct udp_pcb *pcb, struct pbuf *p, cons switch (type) { case MESSAGE_HANDSHAKE_INITIATION: msg_initiation = (struct message_handshake_initiation *)data; - ESP_LOGI(TAG, "HANDSHAKE_INITIATION: %08x:%d", addr->u_addr.ip4.addr, port); + log_i(TAG "HANDSHAKE_INITIATION: %08x:%d", addr->u_addr.ip4.addr, port); // Check mac1 (and optionally mac2) are correct - note it may internally generate a cookie reply packet if (wireguardif_check_initiation_message(device, msg_initiation, addr, port)) { @@ -575,7 +577,7 @@ void wireguardif_network_rx(void *arg, struct udp_pcb *pcb, struct pbuf *p, cons break; case MESSAGE_HANDSHAKE_RESPONSE: - ESP_LOGI(TAG, "HANDSHAKE_RESPONSE: %08x:%d", addr->u_addr.ip4.addr, port); + log_i(TAG "HANDSHAKE_RESPONSE: %08x:%d", addr->u_addr.ip4.addr, port); msg_response = (struct message_handshake_response *)data; // Check mac1 (and optionally mac2) are correct - note it may internally generate a cookie reply packet @@ -590,7 +592,7 @@ void wireguardif_network_rx(void *arg, struct udp_pcb *pcb, struct pbuf *p, cons break; case MESSAGE_COOKIE_REPLY: - ESP_LOGI(TAG, "COOKIE_REPLY: %08x:%d", addr->u_addr.ip4.addr, port); + log_i(TAG "COOKIE_REPLY: %08x:%d", addr->u_addr.ip4.addr, port); msg_cookie = (struct message_cookie_reply *)data; peer = peer_lookup_by_handshake(device, msg_cookie->receiver); if (peer) { @@ -631,7 +633,7 @@ static err_t wireguard_start_handshake(struct netif *netif, struct wireguard_pee pbuf = wireguardif_initiate_handshake(device, peer, &msg, &result); if (pbuf) { result = wireguardif_peer_output(netif, pbuf, peer); - ESP_LOGI(TAG, "start handshake %08x,%d - %d", peer->ip.u_addr.ip4.addr, peer->port, result); + log_i(TAG "start handshake %08x,%d - %d", peer->ip.u_addr.ip4.addr, peer->port, result); pbuf_free(pbuf); peer->send_handshake = false; peer->last_initiation_tx = wireguard_sys_now(); @@ -788,7 +790,7 @@ err_t wireguardif_add_peer(struct netif *netif, struct wireguardif_peer *p, u8_t } uint32_t t2 = wireguard_sys_now(); - printf("Adding peer took %ums\r\n", (t2-t1)); + log_i(TAG "Adding peer took %ums\r\n", (t2-t1)); if (peer_index) { if (peer) { @@ -892,6 +894,23 @@ static void wireguardif_tmr(void *arg) { } } +void wireguardif_shutdown(struct netif *netif) { + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("state != NULL", (netif->state != NULL)); + + struct wireguard_device * device = (struct wireguard_device *)netif->state; + // Disable timer. + sys_untimeout(wireguardif_tmr, device); + // remove UDP context. + if( device->udp_pcb ) { + udp_disconnect(device->udp_pcb); + udp_remove(device->udp_pcb); + device->udp_pcb = NULL; + } + // remove device context. + free(device); + netif->state = NULL; +} err_t wireguardif_init(struct netif *netif) { err_t result; @@ -903,13 +922,14 @@ err_t wireguardif_init(struct netif *netif) { struct netif* underlying_netif; tcpip_adapter_get_netif(TCPIP_ADAPTER_IF_STA, &underlying_netif); - ESP_LOGI(TAG, "underlying_netif = %p", underlying_netif); + log_i(TAG "underlying_netif = %p", underlying_netif); LWIP_ASSERT("netif != NULL", (netif != NULL)); LWIP_ASSERT("state != NULL", (netif->state != NULL)); // We need to initialise the wireguard module wireguard_init(); + log_i(TAG "wireguard module initialized."); if (netif && netif->state) { @@ -934,11 +954,12 @@ err_t wireguardif_init(struct netif *netif) { //udp_bind_netif(udp, underlying_netif); device->udp_pcb = udp; + log_d(TAG "start device initialization"); // Per-wireguard netif/device setup uint32_t t1 = wireguard_sys_now(); if (wireguard_device_init(device, private_key)) { uint32_t t2 = wireguard_sys_now(); - printf("Device init took %ums\r\n", (t2-t1)); + log_d(TAG "Device init took %ums\r\n", (t2-t1)); #if LWIP_CHECKSUM_CTRL_PER_NETIF NETIF_SET_CHECKSUM_CTRL(netif, NETIF_CHECKSUM_ENABLE_ALL); @@ -961,26 +982,32 @@ err_t wireguardif_init(struct netif *netif) { result = ERR_OK; } else { + log_e(TAG "failed to initialize WireGuard device."); mem_free(device); device = NULL; udp_remove(udp); result = ERR_ARG; } } else { + log_e(TAG "failed to allocate device context."); udp_remove(udp); result = ERR_MEM; } } else { + log_e(TAG "failed to bind UDP err=%d", result); udp_remove(udp); } } else { + log_e(TAG "failed to allocate UDP"); result = ERR_MEM; } } else { + log_e(TAG "invalid init_data private key"); result = ERR_ARG; } } else { + log_e(TAG "netif or state is NULL: netif=%p, netif.state:%p", netif, netif ? netif->state : NULL); result = ERR_ARG; } return result; diff --git a/src/wireguardif.h b/src/wireguardif.h index 4040c84..ad9d4a1 100644 --- a/src/wireguardif.h +++ b/src/wireguardif.h @@ -109,6 +109,9 @@ struct wireguardif_peer { // Initialise a new WireGuard network interface (netif) err_t wireguardif_init(struct netif *netif); +// Shutdown a WireGuard network interface (netif) +void wireguardif_shutdown(struct netif *netif); + // Helper to initialise the peer struct with defaults void wireguardif_peer_init(struct wireguardif_peer *peer);