smartalock / wireguard-lwip

WireGuard Implementation for lwIP
Other
192 stars 29 forks source link

Crash on esp8266 arduino ide #2

Closed rjjrbatarao closed 2 years ago

rjjrbatarao commented 2 years ago

I was trying to port this to esp8266, compiled and uploads fine but i get constant crash and reboot, here is the decoded stack trace, my board version 2.6.3

Exception 9: LoadStoreAlignmentCause: Load or store to an unaligned address
PC: 0x402047fc: mul at C:\Users\lan\Documents\ARDUINO_SKETCHES\ESP_WIREGUARD/x25519.c line 61
EXCVADDR: 0x3ffe87dd

Decoding stack results
0x402048b4: mul1 at C:\Users\lan\Documents\ARDUINO_SKETCHES\ESP_WIREGUARD/x25519.c line 156
0x40204a02: x25519 at C:\Users\lan\Documents\ARDUINO_SKETCHES\ESP_WIREGUARD/x25519.c line 157
0x40202836: wireguard_generate_public_key at C:\Users\lan\Documents\ARDUINO_SKETCHES\ESP_WIREGUARD/wireguard.c line 415
0x40203885: wireguard_device_init at C:\Users\lan\Documents\ARDUINO_SKETCHES\ESP_WIREGUARD/wireguard.c line 986
0x402045fe: wireguardif_init at C:\Users\lan\Documents\ARDUINO_SKETCHES\ESP_WIREGUARD/wireguardif.c line 920
0x4021315c: netif_set_addr_LWIP2 at core/netif.c line 717
0x40213210: netif_add_LWIP2 at core/netif.c line 375
0x40204550: wireguardif_init at C:\Users\lan\Documents\ARDUINO_SKETCHES\ESP_WIREGUARD/wireguardif.c line 881
0x4010091f: free(void*) at C:\Users\lan\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.6.3\cores\esp8266\umm_malloc\umm_malloc.cpp line 362
0x40204f73: WireGuard::begin(IPAddress const&, char const*, char const*, char const*, unsigned short) at C:\Users\lan\Documents\ARDUINO_SKETCHES\ESP_WIREGUARD/ESP_WIREGUARD.ino line 45
0x40217d40: ip4_input at core/ipv4/ip4.c line 1167
0x4020691e: __delay(unsigned long) at C:\Users\lan\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.6.3\cores\esp8266\core_esp8266_wiring.cpp line 54
0x4020509d: setup() at C:\Users\lan\Documents\ARDUINO_SKETCHES\ESP_WIREGUARD/ESP_WIREGUARD.ino line 98
0x4020647c: loop_wrapper() at C:\Users\lan\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.6.3\cores\esp8266\core_esp8266_main.cpp line 177

here is the sketch all crypto and wireguard were placed on the root of the sketch.

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <IPAddress.h>
#include "lwip/err.h"
#include "lwip/sys.h"
#include "lwip/ip.h"
#include "lwip/netdb.h"

extern "C" {
#include "wireguardif.h"
#include "wireguard-platform.h"
}

// Wireguard instance
static struct netif wg_netif_struct = {0};
static struct netif *wg_netif = NULL;
static uint8_t wireguard_peer_index = WIREGUARDIF_INVALID_INDEX;

char private_key[] = "8BU1giso23adjCk93dnpLJnK788bRAtpZxs8d+Jo+Vg=";  // [Interface] PrivateKey
IPAddress local_ip(192, 168, 0, 111);         // [Interface] Address
char public_key[] = "x6tAoOCzdZxB2x8un4NxcW6zCbtZ9tSeSAjbS1VZPQ8=";     // [Peer] PublicKey
char endpoint_address[] = "link.arc.soracom.io";    // [Peer] Endpoint
int endpoint_port = 13231;              // [Peer] Endpoint

class WireGuard
{
  public:
    void begin(const IPAddress& localIP, const char* privateKey, const char* remotePeerAddress, const char* remotePeerPublicKey, uint16_t remotePeerPort);
};

void 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<uint32_t>(localIP));
  ip_addr_t netmask = IPADDR4_INIT_BYTES(255, 255, 255, 0);
  ip_addr_t gateway = IPADDR4_INIT_BYTES(0, 0, 0, 0);

  // Setup the WireGuard device structure
  wg.private_key = privateKey;
  wg.listen_port = remotePeerPort;//51820;
  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
  ip_addr_t endpoint_ip = IPADDR4_INIT_BYTES(10, 10, 10, 1);
  peer.endpoint_ip = endpoint_ip;
  peer.endport_port = 13231;

  // Initialize the platform
  wireguard_platform_init();

  // Register the new WireGuard peer with the netwok interface
  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
    wireguardif_connect(wg_netif, wireguard_peer_index);
    netif_set_default(wg_netif);
  }
}

static WireGuard wireg;

void setup() {
  // put your setup code here, to run once:
  Serial.println("Connected. Initializing WireGuard...");
  Serial.begin(115200);
  Serial.println("Connecting to the AP...");

  WiFi.mode(WIFI_STA);
  WiFi.begin("MikroTik1", "");

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("Connected. Initializing WireGuard...");
  delay( 5000 );
  wireg.begin(
    local_ip,
    private_key,
    endpoint_address,
    public_key,
    endpoint_port);
}

void loop() {
  // put your main code here, to run repeatedly:

}
smartalock commented 2 years ago

According to the stack trace the crash is due to unaligned access - the internal structures returned by LwIP require natural alignment to work with the WireGuard library.

I suspect the root issue is that the LwIP #define MEM_ALIGNMENT is set incorrectly in your code. Try looking in your LwIP config files (normally lwipopts.h) to set, e.g. #define MEM_ALIGNMENT 4. It's probably set at 1 which is the reason for the crash.

mcspr commented 2 years ago

Hm. I believe the issue is in the reference x25519 implementation, where it aliases u8 <-> u32 params and not something inside of the LWIP stack. One needs to carefully walk through the call chain to ensure those don't break the alignment up to this point (x25519.c:61) https://github.com/smartalock/wireguard-lwip/blob/4ca93fb0cd2588d4c568ed67a2b977659d5e6711/src/crypto/refc/x25519.c#L58-L64

ref. these noise-c port crash dumps, I thought this looked familiar enough https://github.com/esphome/issues/issues/2502#issuecomment-934268051 https://github.com/esphome/noise-c/blob/3e88febd5ccf024d138addab727540df3d6aa246/src/crypto/x25519/x25519.c#L263

I suspect the root issue is that the LwIP #define MEM_ALIGNMENT is set incorrectly in your code. Try looking in your LwIP config files (normally lwipopts.h) to set, e.g. #define MEM_ALIGNMENT 4. It's probably set at 1 which is the reason for the crash.

btw, Arduino pre-built lwip already sets this https://github.com/d-a-v/esp82xx-nonos-linklayer/blob/18975ca6c4facb2d57de706bec0843f286b7b0e2/glue-lwip/arduino/lwipopts.h#L292

smartalock commented 2 years ago

Hm. I believe the issue is in the reference x25519 implementation, where it aliases u8 <-> u32 params and not something inside of the LWIP stack. One needs to carefully walk through the call chain to ensure those don't break the alignment up to this point (x25519.c:61)

Well spotted! That looks to be exactly the same problem. Their solution was to either compile the code in debug mode which made the compiler generate code that didn't crash, or to change to using a different crypto library that doesn't crash on ESP32

It looks like they switched to libsodium - this shouldn't be too hard to add as an option but I am unfamiliar with ESP32 on how to import this.

Maybe try changing the #define for define for wireguard_x25519 in crypto.h to use the libsodium version - e.g. #define wireguard_x25519(a,b,c) crypto_scalarmult_curve25519(a, b, c)

You'll probably also need to call sodium_init() in your startup code as well.

rjjrbatarao commented 2 years ago

@mcspr @smartalock thank you giving some insights to the problem, i will try to look on the implementation of libsodium and hope that it will work this time.

rjjrbatarao commented 2 years ago

I couldn't implement the libsodium since theres no port to esp8266 arduino ide and its src files are so confusing to include in the sketch. Good news is that i've fixed the crash through libhydrogens x25519 implementation Device init and Adding peer shows up, One thing I noticed is that libhydrogen x25519 was almost identical to your x25519 with the exemption that its mostly renamed to hydrogen_ and with added typecast to some return variable, maybe thats what i missed? i never investigated much maybe someone could look on to this further. Here is the x25519 i used: https://github.com/jedisct1/libhydrogen/blob/4a477649e679aff15480f88d8dc93913540bd70e/impl/x25519.h I deleted the x25519.c since its all included in the .h from libhydrogens implementation. I also added the following on top since it shows some errors when I try to compile from arduino ide saying its missing. I dont know if its actually working lol i just fixed the crashing problem, my next step now is to test this to connect to another device.

static inline void
mem_zero(void *dst_, size_t n)
{
    unsigned char *dst = (unsigned char *) dst_;
    size_t         i;

    for (i = 0; i < n; i++) {
        dst[i] = 0;
    }
}

#define LOAD32_LE(SRC) load32_le(SRC)
static inline uint32_t
load32_le(const uint8_t src[4])
{
#ifdef NATIVE_LITTLE_ENDIAN
    uint32_t w;
    memcpy(&w, src, sizeof w);
    return w;
#else
    uint32_t w = (uint32_t) src[0];
    w |= (uint32_t) src[1] << 8;
    w |= (uint32_t) src[2] << 16;
    w |= (uint32_t) src[3] << 24;
    return w;
#endif
}

#define hydro_x25519_BYTES          32
#define hydro_x25519_PUBLICKEYBYTES 32
#define hydro_x25519_SECRETKEYBYTES 32
trombik commented 2 years ago

just FYI: the original code does work with ESP8266 RTOS SDK v3.4.