1130 lines
39 KiB
C++
1130 lines
39 KiB
C++
/*
|
|
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
|
|
* Copyright (c) 2021 Kenta Ida (fuga@fugafuga.org)
|
|
* Copyright (c) 2023-2024 Simone Rossetto <simros85@gmail.com>
|
|
* Copyright (c) 2025 Stephan Hadinger
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice, this
|
|
* list of conditions and the following disclaimer in the documentation and/or
|
|
* other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
|
|
* its contributors may be used to endorse or promote products derived from this
|
|
* software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* Author: Daniel Hope <daniel.hope@smartalock.com>
|
|
*/
|
|
|
|
#include "wireguardif.h"
|
|
#include <Arduino.h>
|
|
#include <IPAddress.h>
|
|
|
|
#include "lwip/netif.h"
|
|
#include "lwip/ip.h"
|
|
#include "lwip/udp.h"
|
|
#include "lwip/mem.h"
|
|
#include "lwip/sys.h"
|
|
#include "lwip/timeouts.h"
|
|
|
|
#include "esp_wireguard_err.h"
|
|
|
|
#ifdef ESP32
|
|
#include "esp_netif.h"
|
|
#endif
|
|
|
|
#include "wireguard.h"
|
|
#include "crypto.h"
|
|
|
|
#define WIREGUARDIF_TIMER_MSECS 400
|
|
|
|
//**************************************************************************************************************
|
|
// enable AddLog support within a C++ library
|
|
extern void AddLog(uint32_t loglevel, PGM_P formatP, ...);
|
|
enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE};
|
|
uint32_t HighestLogLevel(void);
|
|
//**************************************************************************************************************
|
|
|
|
|
|
static void update_peer_addr(wireguard_peer_t *peer, const ip_addr_t *addr, u16_t port) {
|
|
peer->ip = *addr;
|
|
peer->port = port;
|
|
}
|
|
|
|
static wireguard_peer_t *peer_lookup_by_allowed_ip(wireguard_device_t *device, const ip_addr_t *ipaddr) {
|
|
for (int32_t x=0; x < WIREGUARD_MAX_PEERS; x++) {
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: peer_lookup_by_allowed_ip peer %i (valid %i active %i) ip %_I", x, tmp->valid, tmp->active, ipaddr->u_addr.ip4.addr);
|
|
if (device->peers[x].valid) {
|
|
for (int32_t y=0; y < WIREGUARD_MAX_SRC_IPS; y++) {
|
|
wireguard_allowed_ip *allowed_ip = &device->peers[x].allowed_source_ips[y];
|
|
if (allowed_ip->valid) {
|
|
// AddLog(LOG_LEVEL_DEBUG, "WG : filter ip#%i %_I ip %_I mask %_I", y, allowed_ip->ip.u_addr.ip4.addr, allowed_ip->mask.u_addr.ip4.addr, allowed_ip->mask.u_addr.ip4.addr);
|
|
if (ip_addr_netcmp(ipaddr, &allowed_ip->ip, ip_2_ip4(&allowed_ip->mask))) {
|
|
// AddLog(LOG_LEVEL_DEBUG, "WG : peer_lookup_by_allowed_ip MATCH");
|
|
return &device->peers[x];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static bool wireguardif_can_send_initiation(wireguard_peer_t *peer) {
|
|
return ((peer->last_initiation_tx == 0) || (wireguard_expired(peer->last_initiation_tx, REKEY_TIMEOUT)));
|
|
}
|
|
|
|
static err_t wireguardif_peer_output(struct netif *netif, struct pbuf *q, wireguard_peer_t *peer) {
|
|
wireguard_device_t *device = (wireguard_device_t*) netif->state;
|
|
// Send to last know port, not the connect port
|
|
//TODO: Support DSCP and ECN - lwip requires this set on PCB globally, not per packet
|
|
return udp_sendto_if(device->udp_pcb, q, &peer->ip, peer->port, device->underlying_netif);
|
|
}
|
|
|
|
static err_t wireguardif_device_output(wireguard_device_t *device, struct pbuf *q, const ip_addr_t *ipaddr, u16_t port) {
|
|
return udp_sendto_if(device->udp_pcb, q, ipaddr, port, device->underlying_netif);
|
|
}
|
|
|
|
static err_t wireguardif_output_to_peer(struct netif *netif, struct pbuf *q, const ip_addr_t *ipaddr, wireguard_peer_t *peer) {
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: wireguardif_output_to_peer");
|
|
// The LWIP IP layer wants to send an IP packet out over the interface - we need to encrypt and send it to the peer
|
|
struct message_transport_data *hdr;
|
|
struct pbuf *pbuf;
|
|
err_t result;
|
|
size_t unpadded_len;
|
|
size_t padded_len;
|
|
size_t header_len = 16;
|
|
uint8_t *dst;
|
|
uint32_t now;
|
|
wireguard_keypair_t *keypair = &peer->curr_keypair;
|
|
|
|
// Note: We may not be able to use the current keypair if we haven't received data, may need to resort to using previous keypair
|
|
if (keypair->valid && (!keypair->initiator) && (keypair->last_rx == 0)) {
|
|
keypair = &peer->prev_keypair;
|
|
}
|
|
|
|
if (keypair->valid && (keypair->initiator || keypair->last_rx != 0)) {
|
|
|
|
if (
|
|
!wireguard_expired(keypair->keypair_millis, REJECT_AFTER_TIME) &&
|
|
(keypair->sending_counter < REJECT_AFTER_MESSAGES)
|
|
) {
|
|
|
|
// Calculate the outgoing packet size - round up to next 16 bytes, add 16 bytes for header
|
|
if (q) {
|
|
// This is actual transport data
|
|
unpadded_len = q->tot_len;
|
|
} else {
|
|
// This is a keep-alive
|
|
unpadded_len = 0;
|
|
}
|
|
padded_len = (unpadded_len + 15) & 0xFFFFFFF0; // Round up to next 16 byte boundary
|
|
|
|
// The buffer needs to be allocated from "transport" pool to leave room for LwIP generated IP headers
|
|
// 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) {
|
|
// 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);
|
|
|
|
hdr = (struct message_transport_data *)pbuf->payload;
|
|
|
|
hdr->type = MESSAGE_TRANSPORT_DATA;
|
|
hdr->receiver = keypair->remote_index;
|
|
// Alignment required... pbuf_alloc has probably aligned data, but want to be sure
|
|
U64TO8_LITTLE(hdr->counter, keypair->sending_counter);
|
|
|
|
// Copy the encrypted (padded) data to the output packet - chacha20poly1305_encrypt() can encrypt data in-place which avoids call to mem_malloc
|
|
dst = &hdr->enc_packet[0];
|
|
if ((padded_len > 0) && q) {
|
|
// Note: before copying make sure we have inserted the IP header checksum
|
|
// The IP header checksum (and other checksums in the IP packet - e.g. ICMP) need to be calculated by LWIP before calling
|
|
// The Wireguard interface always needs checksums to be generated in software but the base netif may have some checksums generated by hardware
|
|
|
|
// Copy pbuf to memory - handles case where pbuf is chained
|
|
pbuf_copy_partial(q, dst, unpadded_len, 0);
|
|
}
|
|
|
|
// Then encrypt
|
|
struct ip_hdr *iphdr = (struct ip_hdr *)dst;
|
|
// AddLog(LOG_LEVEL_DEBUG, "WG : Send inner addr src %_I dest %_I proto %i %*_H", iphdr->src, iphdr->dest, iphdr->_proto, padded_len, dst);
|
|
|
|
wireguard_encrypt_packet(dst, dst, padded_len, keypair);
|
|
|
|
result = wireguardif_peer_output(netif, pbuf, peer);
|
|
// AddLog(LOG_LEVEL_DEBUG, "WG : send packet to %_I:%i result %i", peer->ip.u_addr.ip4.addr, peer->port, result);
|
|
|
|
if (result == ERR_OK) {
|
|
now = wireguard_sys_now();
|
|
peer->last_tx = now;
|
|
keypair->last_tx = now;
|
|
}
|
|
|
|
pbuf_free(pbuf);
|
|
|
|
// Check to see if we should rekey
|
|
if (keypair->sending_counter >= REKEY_AFTER_MESSAGES) {
|
|
peer->send_handshake = true;
|
|
} else if (keypair->initiator && wireguard_expired(keypair->keypair_millis, REKEY_AFTER_TIME)) {
|
|
peer->send_handshake = true;
|
|
}
|
|
|
|
} else {
|
|
// Failed to allocate memory
|
|
result = ERR_MEM;
|
|
}
|
|
} else {
|
|
// key has expired...
|
|
keypair_destroy(keypair);
|
|
result = ERR_CONN;
|
|
}
|
|
} else {
|
|
// No valid keys!
|
|
result = ERR_CONN;
|
|
}
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: OUT wireguardif_output_to_peer err %d", result);
|
|
return result;
|
|
}
|
|
|
|
// This is used as the output function for the Wireguard netif
|
|
// The ipaddr here is the one inside the VPN which we use to lookup the correct peer/endpoint
|
|
static err_t wireguardif_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ip4addr) {
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: wireguardif_output state %p", netif->state);
|
|
if (netif->state == NULL) {
|
|
// maybe the underlying interface has already been deactivated?
|
|
return ERR_IF;
|
|
}
|
|
wireguard_device_t *device = (wireguard_device_t *)netif->state;
|
|
ip_addr_t ipaddr;
|
|
|
|
if (!device) {
|
|
// AddLog(LOG_LEVEL_INFO, ">>>: wireguardif_output NULL device");
|
|
return ERR_RTE;
|
|
}
|
|
|
|
// Send to peer that matches dest IP
|
|
ip_addr_copy_from_ip4(ipaddr, *ip4addr);
|
|
wireguard_peer_t *peer = peer_lookup_by_allowed_ip(device, &ipaddr);
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: wireguardif_output ipaddr %s peer %p", IPAddress(&ipaddr).toString().c_str(), peer);
|
|
if (peer) {
|
|
return wireguardif_output_to_peer(netif, q, &ipaddr, peer);
|
|
} else {
|
|
return ERR_RTE;
|
|
}
|
|
}
|
|
|
|
static void wireguardif_send_keepalive(wireguard_device_t *device, wireguard_peer_t *peer) {
|
|
if (HighestLogLevel() >= LOG_LEVEL_DEBUG) {
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("WG : Sending keep-alive"));
|
|
}
|
|
// Send a NULL packet as a keep-alive
|
|
wireguardif_output_to_peer(device->netif, NULL, NULL, peer);
|
|
}
|
|
|
|
static void wireguardif_process_response_message(wireguard_device_t *device, wireguard_peer_t *peer, struct message_handshake_response *response, const ip_addr_t *addr, u16_t port) {
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: wireguardif_process_response_message");
|
|
if (wireguard_process_handshake_response(device, peer, response)) {
|
|
// Packet is good
|
|
// Update the peer location
|
|
update_peer_addr(peer, addr, port);
|
|
|
|
wireguard_start_session(peer, true);
|
|
wireguardif_send_keepalive(device, peer);
|
|
|
|
// Set the IF-UP flag on netif
|
|
netif_set_link_up(device->netif);
|
|
} else {
|
|
// Packet bad
|
|
}
|
|
}
|
|
|
|
static bool peer_add_ip(wireguard_peer_t *peer, const ip_addr_t& ip, const ip_addr_t& mask) {
|
|
bool result = false;
|
|
struct wireguard_allowed_ip *allowed;
|
|
int x;
|
|
// Look for existing match first
|
|
for (x=0; x < WIREGUARD_MAX_SRC_IPS; x++) {
|
|
allowed = &peer->allowed_source_ips[x];
|
|
if ((allowed->valid) && ip_addr_cmp(&allowed->ip, &ip) && ip_addr_cmp(&allowed->mask, &mask)) {
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!result) {
|
|
// Look for a free slot
|
|
for (x=0; x < WIREGUARD_MAX_SRC_IPS; x++) {
|
|
allowed = &peer->allowed_source_ips[x];
|
|
if (!allowed->valid) {
|
|
allowed->valid = true;
|
|
allowed->ip = ip;
|
|
allowed->mask = mask;
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static void wireguardif_process_data_message(wireguard_device_t *device, wireguard_peer_t *peer, struct message_transport_data *data_hdr, size_t data_len, const ip_addr_t *addr, u16_t port) {
|
|
wireguard_keypair_t *keypair;
|
|
uint64_t nonce;
|
|
uint8_t *src;
|
|
size_t src_len;
|
|
struct pbuf *pbuf;
|
|
struct ip_hdr *iphdr;
|
|
ip_addr_t dest;
|
|
bool dest_ok = false;
|
|
int x;
|
|
uint32_t now;
|
|
uint16_t header_len = 0xFFFF;
|
|
uint32_t idx = data_hdr->receiver;
|
|
|
|
keypair = get_peer_keypair_for_idx(peer, idx);
|
|
|
|
if (keypair) {
|
|
if (
|
|
(keypair->receiving_valid) &&
|
|
!wireguard_expired(keypair->keypair_millis, REJECT_AFTER_TIME) &&
|
|
(keypair->sending_counter < REJECT_AFTER_MESSAGES)
|
|
|
|
) {
|
|
|
|
nonce = U8TO64_LITTLE(data_hdr->counter);
|
|
src = &data_hdr->enc_packet[0];
|
|
src_len = data_len;
|
|
|
|
// We don't know the unpadded size until we have decrypted the packet and validated/inspected the IP header
|
|
pbuf = pbuf_alloc(PBUF_TRANSPORT, src_len - WIREGUARD_AUTHTAG_LEN, PBUF_RAM);
|
|
if (pbuf) {
|
|
// Decrypt the packet
|
|
memset(pbuf->payload, 0, pbuf->tot_len);
|
|
if (wireguard_decrypt_packet((uint8_t*)pbuf->payload, src, src_len, nonce, keypair)) {
|
|
|
|
// 3. Since the packet has authenticated correctly, the source IP of the outer UDP/IP packet is used to update the endpoint for peer TrMv...WXX0.
|
|
// Update the peer location
|
|
update_peer_addr(peer, addr, port);
|
|
// AddLog(LOG_LEVEL_DEBUG, "WG : received (%_I:%i) %*_H", addr->u_addr.ip4.addr, port, src_len, pbuf->payload);
|
|
|
|
now = wireguard_sys_now();
|
|
keypair->last_rx = now;
|
|
peer->last_rx = now;
|
|
|
|
// Might need to shuffle next key --> current keypair
|
|
keypair_update(peer, keypair);
|
|
|
|
// Check to see if we should rekey
|
|
if (keypair->initiator && wireguard_expired(keypair->keypair_millis, REJECT_AFTER_TIME - peer->keepalive_interval - REKEY_TIMEOUT)) {
|
|
peer->send_handshake = true;
|
|
}
|
|
|
|
// Make sure that link is reported as up
|
|
netif_set_link_up(device->netif);
|
|
|
|
if (pbuf->tot_len > 0) {
|
|
//4a. Once the packet payload is decrypted, the interface has a plaintext packet. If this is not an IP packet, it is dropped.
|
|
iphdr = (struct ip_hdr *)pbuf->payload;
|
|
// Check for packet replay / dupes
|
|
if (wireguard_check_replay(keypair, nonce)) {
|
|
|
|
// 4b. Otherwise, WireGuard checks to see if the source IP address of the plaintext inner-packet routes correspondingly in the cryptokey routing table
|
|
// Also check packet length!
|
|
#if LWIP_IPV4
|
|
if (IPH_V(iphdr) == 4) {
|
|
ip_addr_copy_from_ip4(dest, iphdr->dest);
|
|
// AddLog(LOG_LEVEL_DEBUG, "WG : inner addr src %_I dest %_I proto %i", iphdr->src, iphdr->dest, iphdr->_proto);
|
|
for (x=0; x < WIREGUARD_MAX_SRC_IPS; x++) {
|
|
if (peer->allowed_source_ips[x].valid) {
|
|
if (ip_addr_netcmp(&dest, &peer->allowed_source_ips[x].ip, ip_2_ip4(&peer->allowed_source_ips[x].mask))) {
|
|
dest_ok = true;
|
|
header_len = PP_NTOHS(IPH_LEN(iphdr));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif /* LWIP_IPV4 */
|
|
#if LWIP_IPV6
|
|
if (IPH_V(iphdr) == 6) {
|
|
// TODO: IPV6 support for route filtering
|
|
header_len = PP_NTOHS(IPH_LEN(iphdr));
|
|
dest_ok = true;
|
|
}
|
|
#endif /* LWIP_IPV6 */
|
|
if (header_len <= pbuf->tot_len) {
|
|
|
|
// 5. If the plaintext packet has not been dropped, it is inserted into the receive queue of the wg0 interface.
|
|
if (dest_ok) {
|
|
// Send packet to be process by LWIP
|
|
ip_input(pbuf, device->netif);
|
|
// pbuf is owned by IP layer now
|
|
pbuf = NULL;
|
|
}
|
|
} else {
|
|
// IP header is corrupt or lied about packet size
|
|
}
|
|
} else {
|
|
// AddLog(LOG_LEVEL_DEBUG, "WG : Duplicate packet");
|
|
// This is a duplicate packet / replayed / too far out of order
|
|
}
|
|
} else {
|
|
if (HighestLogLevel() >= LOG_LEVEL_DEBUG) {
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("WG : Received keep-alive"));
|
|
}
|
|
// This was a keep-alive packet
|
|
}
|
|
}
|
|
|
|
if (pbuf) {
|
|
pbuf_free(pbuf);
|
|
}
|
|
}
|
|
|
|
|
|
} else {
|
|
//After Reject-After-Messages transport data messages or after the current secure session is Reject- After-Time seconds old,
|
|
// whichever comes first, WireGuard will refuse to send or receive any more transport data messages using the current secure session,
|
|
// until a new secure session is created through the 1-RTT handshake
|
|
keypair_destroy(keypair);
|
|
}
|
|
|
|
} else {
|
|
// Could not locate valid keypair for remote index
|
|
}
|
|
}
|
|
|
|
static struct pbuf *wireguardif_initiate_handshake(wireguard_device_t *device, wireguard_peer_t *peer, struct message_handshake_initiation *msg, err_t *error) {
|
|
// AddLog(LOG_LEVEL_INFO, ">>>: wireguardif_initiate_handshake");
|
|
struct pbuf *pbuf = NULL;
|
|
err_t err = ERR_OK;
|
|
if (wireguard_create_handshake_initiation(device, peer, msg)) {
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: sending initiation packet");
|
|
pbuf = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct message_handshake_initiation), PBUF_RAM);
|
|
if (pbuf) {
|
|
err = pbuf_take(pbuf, msg, sizeof(struct message_handshake_initiation));
|
|
if (err == ERR_OK) {
|
|
// OK!
|
|
} else {
|
|
pbuf_free(pbuf);
|
|
pbuf = NULL;
|
|
}
|
|
} else {
|
|
err = ERR_MEM;
|
|
}
|
|
} else {
|
|
err = ERR_ARG;
|
|
}
|
|
if (error) {
|
|
*error = err;
|
|
}
|
|
// AddLog(LOG_LEVEL_INFO, ">>>: OUT wireguardif_initiate_handshake pbuf %p", pbuf);
|
|
return pbuf;
|
|
}
|
|
|
|
static void wireguardif_send_handshake_response(wireguard_device_t *device, wireguard_peer_t *peer) {
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: wireguardif_send_handshake_response");
|
|
struct message_handshake_response packet;
|
|
struct pbuf *pbuf = NULL;
|
|
err_t err = ERR_OK;
|
|
|
|
if (wireguard_create_handshake_response(device, peer, &packet)) {
|
|
|
|
wireguard_start_session(peer, false);
|
|
|
|
// AddLog(LOG_LEVEL_DEBUG, "WG : sending handshake response packet");
|
|
pbuf = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct message_handshake_response), PBUF_RAM);
|
|
if (pbuf) {
|
|
err = pbuf_take(pbuf, &packet, sizeof(struct message_handshake_response));
|
|
if (err == ERR_OK) {
|
|
// OK!
|
|
wireguardif_peer_output(device->netif, pbuf, peer);
|
|
}
|
|
pbuf_free(pbuf);
|
|
}
|
|
}
|
|
}
|
|
|
|
static size_t get_source_addr_port(const ip_addr_t *addr, u16_t port, uint8_t *buf, size_t buflen) {
|
|
size_t result = 0;
|
|
|
|
#if LWIP_IPV4
|
|
if (IP_IS_V4(addr) && (buflen >= 4)) {
|
|
U32TO8_BIG(buf + result, PP_NTOHL(ip4_addr_get_u32(ip_2_ip4(addr))));
|
|
result += 4;
|
|
}
|
|
#endif
|
|
#if LWIP_IPV6
|
|
if (IP_IS_V6(addr) && (buflen >= 16)) {
|
|
U16TO8_BIG(buf + result + 0, IP6_ADDR_BLOCK1(ip_2_ip6(addr)));
|
|
U16TO8_BIG(buf + result + 2, IP6_ADDR_BLOCK2(ip_2_ip6(addr)));
|
|
U16TO8_BIG(buf + result + 4, IP6_ADDR_BLOCK3(ip_2_ip6(addr)));
|
|
U16TO8_BIG(buf + result + 6, IP6_ADDR_BLOCK4(ip_2_ip6(addr)));
|
|
U16TO8_BIG(buf + result + 8, IP6_ADDR_BLOCK5(ip_2_ip6(addr)));
|
|
U16TO8_BIG(buf + result + 10, IP6_ADDR_BLOCK6(ip_2_ip6(addr)));
|
|
U16TO8_BIG(buf + result + 12, IP6_ADDR_BLOCK7(ip_2_ip6(addr)));
|
|
U16TO8_BIG(buf + result + 14, IP6_ADDR_BLOCK8(ip_2_ip6(addr)));
|
|
result += 16;
|
|
}
|
|
#endif
|
|
if (buflen >= result + 2) {
|
|
U16TO8_BIG(buf + result, port);
|
|
result += 2;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static void wireguardif_send_handshake_cookie(wireguard_device_t *device, const uint8_t *mac1, uint32_t index, const ip_addr_t *addr, u16_t port) {
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: wireguardif_send_handshake_cookie");
|
|
struct message_cookie_reply packet;
|
|
struct pbuf *pbuf = NULL;
|
|
err_t err = ERR_OK;
|
|
uint8_t source_buf[18];
|
|
size_t source_len = get_source_addr_port(addr, port, source_buf, sizeof(source_buf));
|
|
|
|
wireguard_create_cookie_reply(device, &packet, mac1, index, source_buf, source_len);
|
|
|
|
// Send this packet out!
|
|
pbuf = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct message_cookie_reply), PBUF_RAM);
|
|
if (pbuf) {
|
|
err = pbuf_take(pbuf, &packet, sizeof(struct message_cookie_reply));
|
|
if (err == ERR_OK) {
|
|
wireguardif_device_output(device, pbuf, addr, port);
|
|
}
|
|
pbuf_free(pbuf);
|
|
}
|
|
}
|
|
|
|
static bool wireguardif_check_initiation_message(wireguard_device_t *device, struct message_handshake_initiation *msg, const ip_addr_t *addr, u16_t port) {
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: wireguardif_check_initiation_message");
|
|
bool result = false;
|
|
uint8_t *data = (uint8_t *)msg;
|
|
uint8_t source_buf[18];
|
|
size_t source_len;
|
|
// We received an initiation packet check it is valid
|
|
|
|
if (wireguard_check_mac1(device, data, sizeof(struct message_handshake_initiation) - (2 * WIREGUARD_COOKIE_LEN), msg->mac1)) {
|
|
// mac1 is valid!
|
|
result = true;
|
|
// skip the following since wireguard_is_under_load() is always false
|
|
// if (wireguard_is_under_load()) {
|
|
// // If we are under load then check mac2
|
|
// source_len = get_source_addr_port(addr, port, source_buf, sizeof(source_buf));
|
|
|
|
// result = wireguard_check_mac2(device, data, sizeof(struct message_handshake_initiation) - (WIREGUARD_COOKIE_LEN), source_buf, source_len, msg->mac2);
|
|
|
|
// if (!result) {
|
|
// // mac2 is invalid (cookie may have expired) or not present
|
|
// // 5.3 Denial of Service Mitigation & Cookies
|
|
// // If the responder receives a message with a valid msg.mac1 yet with an invalid msg.mac2, and is under load, it may respond with a cookie reply message
|
|
// wireguardif_send_handshake_cookie(device, msg->mac1, msg->sender, addr, port);
|
|
// }
|
|
// }
|
|
|
|
} else {
|
|
// mac1 is invalid
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static bool wireguardif_check_response_message(wireguard_device_t *device, struct message_handshake_response *msg, const ip_addr_t *addr, u16_t port) {
|
|
bool result = false;
|
|
uint8_t *data = (uint8_t *)msg;
|
|
uint8_t source_buf[18];
|
|
size_t source_len;
|
|
// We received an initiation packet check it is valid
|
|
|
|
if (wireguard_check_mac1(device, data, sizeof(struct message_handshake_response) - (2 * WIREGUARD_COOKIE_LEN), msg->mac1)) {
|
|
// mac1 is valid!
|
|
result = true;
|
|
// skip the following since wireguard_is_under_load() is always false
|
|
// if (wireguard_is_under_load()) {
|
|
// // If we are under load then check mac2
|
|
// source_len = get_source_addr_port(addr, port, source_buf, sizeof(source_buf));
|
|
|
|
// result = wireguard_check_mac2(device, data, sizeof(struct message_handshake_response) - (WIREGUARD_COOKIE_LEN), source_buf, source_len, msg->mac2);
|
|
|
|
// // AddLog(LOG_LEVEL_DEBUG, ">>>: wireguardif_check_response_message MAC2 %d (1==SUCCESS)", result);
|
|
// if (!result) {
|
|
// // mac2 is invalid (cookie may have expired) or not present
|
|
// // 5.3 Denial of Service Mitigation & Cookies
|
|
// // If the responder receives a message with a valid msg.mac1 yet with an invalid msg.mac2, and is under load, it may respond with a cookie reply message
|
|
// wireguardif_send_handshake_cookie(device, msg->mac1, msg->sender, addr, port);
|
|
// }
|
|
// }
|
|
|
|
} else {
|
|
// mac1 is invalid
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
void wireguardif_network_rx(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) {
|
|
// LWIP_ASSERT("wireguardif_network_rx: invalid arg", arg != NULL);
|
|
// LWIP_ASSERT("wireguardif_network_rx: invalid pbuf", p != NULL);
|
|
// We have received a packet from the base_netif to our UDP port - process this as a possible Wireguard packet
|
|
wireguard_device_t *device = (wireguard_device_t *)arg;
|
|
wireguard_peer_t *peer;
|
|
uint8_t *data = (uint8_t*) p->payload;
|
|
size_t len = p->len; // This buf, not chained ones
|
|
|
|
struct message_handshake_initiation *msg_initiation;
|
|
struct message_handshake_response *msg_response;
|
|
struct message_cookie_reply *msg_cookie;
|
|
struct message_transport_data *msg_data;
|
|
|
|
uint8_t type = wireguard_get_message_type(data, len);
|
|
|
|
switch (type) {
|
|
case MESSAGE_HANDSHAKE_INITIATION:
|
|
// AddLog(LOG_LEVEL_DEBUG, "");
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("WG : RX MESSAGE_HANDSHAKE_INITIATION"));
|
|
msg_initiation = (struct message_handshake_initiation *)data;
|
|
|
|
// 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)) {
|
|
|
|
peer = wireguard_process_initiation_message(device, msg_initiation);
|
|
if (peer) {
|
|
// Update the peer location
|
|
update_peer_addr(peer, addr, port);
|
|
|
|
// Send back a handshake response
|
|
wireguardif_send_handshake_response(device, peer);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_HANDSHAKE_RESPONSE:
|
|
// AddLog(LOG_LEVEL_DEBUG, "");
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("WG : RX MESSAGE_HANDSHAKE_RESPONSE from %s:%i"), IPAddress(addr).toString().c_str(), port);
|
|
msg_response = (struct message_handshake_response *)data;
|
|
|
|
// Check mac1 (and optionally mac2) are correct - note it may internally generate a cookie reply packet
|
|
if (wireguardif_check_response_message(device, msg_response, addr, port)) {
|
|
|
|
peer = wireguard_peer_lookup_by_handshake(device, msg_response->receiver);
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: RX MESSAGE_HANDSHAKE_RESPONSE peer %p", peer);
|
|
if (peer) {
|
|
// Process the handshake response
|
|
wireguardif_process_response_message(device, peer, msg_response, addr, port);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_COOKIE_REPLY:
|
|
msg_cookie = (struct message_cookie_reply *)data;
|
|
peer = wireguard_peer_lookup_by_handshake(device, msg_cookie->receiver);
|
|
// AddLog(LOG_LEVEL_DEBUG, "");
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("WG : RX MESSAGE_COOKIE_REPLY peer=%p"), peer);
|
|
if (peer) {
|
|
if (wireguard_process_cookie_message(device, peer, msg_cookie)) {
|
|
// Update the peer location
|
|
update_peer_addr(peer, addr, port);
|
|
|
|
// Don't send anything out - we stay quiet until the next initiation message
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_TRANSPORT_DATA:
|
|
// AddLog(LOG_LEVEL_DEBUG, "");
|
|
msg_data = (struct message_transport_data *)data;
|
|
peer = wireguard_peer_lookup_by_receiver(device, msg_data->receiver);
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("WG : RX MESSAGE_TRANSPORT_DATA from %s"), IPAddress(addr).toString().c_str());
|
|
if (peer) {
|
|
// header is 16 bytes long so take that off the length
|
|
wireguardif_process_data_message(device, peer, msg_data, len - 16, addr, port);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// AddLog(LOG_LEVEL_DEBUG, "");
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("WG : RX UNKNOWN"));
|
|
// Unknown or bad packet header
|
|
break;
|
|
}
|
|
// Release data!
|
|
pbuf_free(p);
|
|
}
|
|
|
|
static err_t wireguard_start_handshake(struct netif *netif, wireguard_peer_t *peer) {
|
|
// AddLog(LOG_LEVEL_INFO, ">>>: wireguard_start_handshake");
|
|
wireguard_device_t *device = (wireguard_device_t *)netif->state;
|
|
err_t result;
|
|
struct pbuf *pbuf;
|
|
struct message_handshake_initiation msg;
|
|
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: starting handshake");
|
|
pbuf = wireguardif_initiate_handshake(device, peer, &msg, &result);
|
|
if (pbuf) {
|
|
// AddLog(LOG_LEVEL_INFO, ">>>: wireguard_start_handshake ready to send packet");
|
|
result = wireguardif_peer_output(netif, pbuf, peer);
|
|
if (result != ERR_OK) {
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: wireguardif_peer_output: %i", result);
|
|
}
|
|
pbuf_free(pbuf);
|
|
peer->send_handshake = false;
|
|
peer->last_initiation_tx = wireguard_sys_now();
|
|
memcpy(peer->handshake_mac1, msg.mac1, WIREGUARD_COOKIE_LEN);
|
|
peer->handshake_mac1_valid = true;
|
|
}
|
|
// AddLog(LOG_LEVEL_INFO, ">>>: OUT wireguard_start_handshake result %d", result);
|
|
return result;
|
|
}
|
|
|
|
static err_t wireguardif_lookup_peer(struct netif *netif, u8_t peer_index, wireguard_peer_t **out) {
|
|
// LWIP_ASSERT("netif != NULL", (netif != NULL));
|
|
// LWIP_ASSERT("state != NULL", (netif->state != NULL));
|
|
wireguard_device_t *device = (wireguard_device_t *)netif->state;
|
|
wireguard_peer_t *peer = NULL;
|
|
err_t result;
|
|
|
|
if (device->valid) {
|
|
peer = wireguard_peer_lookup_by_peer_index(device, peer_index);
|
|
if (peer) {
|
|
result = ERR_OK;
|
|
} else {
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("WG : wireguard_peer_lookup_by_peer_index: peer not found"));
|
|
result = ERR_ARG;
|
|
}
|
|
} else {
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("WG : wireguardif_lookup_peer: invalid device"));
|
|
result = ERR_ARG;
|
|
}
|
|
*out = peer;
|
|
return result;
|
|
}
|
|
|
|
err_t wireguardif_connect(struct netif *netif, u8_t peer_index) {
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: wireguardif_connect %d", peer_index);
|
|
wireguard_peer_t *peer;
|
|
err_t result = wireguardif_lookup_peer(netif, peer_index, &peer);
|
|
if (result == ERR_OK) {
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: wireguardif_connect lookup ok");
|
|
// Check that a valid connect ip and port have been set
|
|
if (!ip_addr_isany(&peer->connect_ip) && (peer->connect_port > 0)) {
|
|
// Set the flag that we want to try connecting
|
|
peer->active = true;
|
|
peer->ip = peer->connect_ip;
|
|
peer->port = peer->connect_port;
|
|
result = ERR_OK;
|
|
} else {
|
|
result = ERR_ARG;
|
|
}
|
|
}
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: OUT wireguardif_connect %d", result);
|
|
return result;
|
|
}
|
|
|
|
err_t wireguardif_disconnect(struct netif *netif, u8_t peer_index) {
|
|
wireguard_peer_t *peer;
|
|
err_t result = wireguardif_lookup_peer(netif, peer_index, &peer);
|
|
if (result == ERR_OK) {
|
|
// Set the flag that we want to try connecting
|
|
peer->active = false;
|
|
// Wipe out current keys
|
|
keypair_destroy(&peer->next_keypair);
|
|
keypair_destroy(&peer->curr_keypair);
|
|
keypair_destroy(&peer->prev_keypair);
|
|
result = ERR_OK;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
err_t wireguardif_peer_is_up(struct netif *netif, u8_t peer_index, ip_addr_t *current_ip, u16_t *current_port) {
|
|
wireguard_peer_t *peer;
|
|
err_t result = wireguardif_lookup_peer(netif, peer_index, &peer);
|
|
if (result == ERR_OK) {
|
|
if ((peer->curr_keypair.valid) || (peer->prev_keypair.valid)) {
|
|
result = ERR_OK;
|
|
} else {
|
|
// AddLog(LOG_LEVEL_DEBUG, "WG : wireguardif_peer_is_up: invalid keypairs");
|
|
result = ERR_CONN;
|
|
}
|
|
if (current_ip) {
|
|
*current_ip = peer->ip;
|
|
}
|
|
if (current_port) {
|
|
*current_port = peer->port;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
time_t wireguardif_latest_handshake(struct netif *netif, u8_t peer_index) {
|
|
time_t result = 0;
|
|
wireguard_peer_t *peer;
|
|
err_t err = wireguardif_lookup_peer(netif, peer_index, &peer);
|
|
if (err == ERR_OK) {
|
|
if (peer->valid && peer->latest_handshake_millis > 0) {
|
|
/*
|
|
* The time() function returns the current timestamp (seconds since epoch),
|
|
* the wireguard_sys_now() function returns milliseconds since device boot up,
|
|
* so their difference is the timestamp (since epoch) of device boot time,
|
|
* so the latest handshake (saved executing wireguard_sys_now) plus timestamp
|
|
* of device boot time is the timestamp (since epoch) of the latest
|
|
* completed handshake. With ~1 second precision.
|
|
*/
|
|
result = (peer->latest_handshake_millis / 1000) + (time(NULL) - (wireguard_sys_now() / 1000));
|
|
} else {
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("WG : wireguardif_latest_handshake: valid=%ld, lhs=%ld"), (long) peer->valid, (long) peer->latest_handshake_millis);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
err_t wireguardif_add_allowed_ip(struct netif *netif, u8_t peer_index, const ip_addr_t& ip, const ip_addr_t& mask) {
|
|
wireguard_peer_t *peer;
|
|
err_t result = wireguardif_lookup_peer(netif, peer_index, &peer);
|
|
if (result == ERR_OK) {
|
|
if(peer_add_ip(peer, ip, mask)) {
|
|
result = ERR_OK;
|
|
} else {
|
|
result = ERR_MEM;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
err_t wireguardif_remove_peer(struct netif *netif, u8_t peer_index) {
|
|
wireguard_peer_t *peer;
|
|
err_t result = wireguardif_lookup_peer(netif, peer_index, &peer);
|
|
if (result == ERR_OK) {
|
|
crypto_zero(peer, sizeof(wireguard_peer_t));
|
|
peer->valid = false;
|
|
result = ERR_OK;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
err_t wireguardif_add_peer(struct netif *netif, wireguardif_peer_t *p, u8_t *peer_index) {
|
|
wireguard_device_t *device = (wireguard_device_t *)netif->state;
|
|
err_t result;
|
|
// uint8_t public_key[WIREGUARD_PUBLIC_KEY_LEN];
|
|
wireguard_peer_t *peer = NULL;
|
|
|
|
uint32_t t1 = wireguard_sys_now();
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: wireguardif_add_peer now %d", t1);
|
|
|
|
|
|
// size_t len = decode_base64_length((unsigned char*)p->public_key);
|
|
// if (len == WIREGUARD_PUBLIC_KEY_LEN) {
|
|
// len = decode_base64((unsigned char*)p->public_key, (unsigned char*)public_key);
|
|
// // AddLog(LOG_LEVEL_DEBUG, ">>>: wireguardif_add_peer public_key %*_H", WIREGUARD_PUBLIC_KEY_LEN, public_key);
|
|
// // See if the peer is already registered
|
|
peer = wireguard_peer_lookup_by_pubkey(device, p->public_key2);
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: wireguardif_add_peer peer %p", peer);
|
|
if (!peer) {
|
|
// Not active - see if we have room to allocate a new one
|
|
peer = wireguard_peer_alloc(device);
|
|
if (peer) {
|
|
|
|
if (wireguard_peer_init(device, peer, p->public_key2, p->preshared_key2)) {
|
|
|
|
peer->connect_ip = p->endpoint_ip;
|
|
peer->connect_port = p->endport_port;
|
|
peer->ip = peer->connect_ip;
|
|
peer->port = peer->connect_port;
|
|
peer->keepalive_interval = p->keep_alive;
|
|
peer_add_ip(peer, p->allowed_ip, p->allowed_mask);
|
|
memcpy(peer->greatest_timestamp, p->greatest_timestamp, sizeof(peer->greatest_timestamp));
|
|
|
|
result = ERR_OK;
|
|
} else {
|
|
result = ERR_ARG;
|
|
}
|
|
} else {
|
|
result = ERR_MEM;
|
|
}
|
|
}
|
|
// } else {
|
|
// result = ERR_OK;
|
|
// }
|
|
// } else {
|
|
// result = ERR_BUF;
|
|
// }
|
|
|
|
uint32_t t2 = wireguard_sys_now();
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: adding took %" PRIu32 "ms", (t2-t1));
|
|
|
|
if (peer_index) {
|
|
if (peer) {
|
|
*peer_index = wireguard_peer_index(device, peer);
|
|
} else {
|
|
*peer_index = WIREGUARDIF_INVALID_INDEX;
|
|
}
|
|
}
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: OUT wireguardif_add_peer %d", result);
|
|
return result;
|
|
}
|
|
|
|
static bool should_send_initiation(wireguard_peer_t *peer) {
|
|
bool result = false;
|
|
if (wireguardif_can_send_initiation(peer)) {
|
|
if (peer->send_handshake) {
|
|
result = true;
|
|
} else if (peer->curr_keypair.valid && !peer->curr_keypair.initiator && wireguard_expired(peer->curr_keypair.keypair_millis, REJECT_AFTER_TIME - peer->keepalive_interval)) {
|
|
result = true;
|
|
} else if (!peer->curr_keypair.valid && peer->active) {
|
|
result = true;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static bool should_send_keepalive(wireguard_peer_t *peer) {
|
|
bool result = false;
|
|
if (peer->keepalive_interval > 0) {
|
|
if ((peer->curr_keypair.valid) || (peer->prev_keypair.valid)) {
|
|
if (wireguard_expired(peer->last_tx, peer->keepalive_interval)) {
|
|
result = true;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static bool should_destroy_current_keypair(wireguard_peer_t *peer) {
|
|
bool result = false;
|
|
if (peer->curr_keypair.valid &&
|
|
(wireguard_expired(peer->curr_keypair.keypair_millis, REJECT_AFTER_TIME) ||
|
|
(peer->curr_keypair.sending_counter >= REJECT_AFTER_MESSAGES))
|
|
) {
|
|
result = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static bool should_reset_peer(wireguard_peer_t *peer) {
|
|
bool result = false;
|
|
if (peer->curr_keypair.valid && (wireguard_expired(peer->curr_keypair.keypair_millis, REJECT_AFTER_TIME * 3))) {
|
|
result = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static void wireguardif_tmr(void *arg) {
|
|
wireguard_device_t *device = (wireguard_device_t *)arg;
|
|
wireguard_peer_t *peer;
|
|
// Reschedule this timer
|
|
sys_timeout(WIREGUARDIF_TIMER_MSECS, wireguardif_tmr, device);
|
|
|
|
// Check periodic things
|
|
bool link_up = false;
|
|
for (int32_t x=0; x < WIREGUARD_MAX_PEERS; x++) {
|
|
peer = &device->peers[x];
|
|
if (peer->valid) {
|
|
// Do we need to rekey / send a handshake?
|
|
if (should_reset_peer(peer)) {
|
|
// Nothing back for too long - we should wipe out all crypto state
|
|
keypair_destroy(&peer->next_keypair);
|
|
keypair_destroy(&peer->curr_keypair);
|
|
keypair_destroy(&peer->prev_keypair);
|
|
// TODO: Also destroy handshake?
|
|
|
|
// Revert back to default IP/port if these were altered
|
|
peer->ip = peer->connect_ip;
|
|
peer->port = peer->connect_port;
|
|
}
|
|
if (should_destroy_current_keypair(peer)) {
|
|
// Destroy current keypair
|
|
keypair_destroy(&peer->curr_keypair);
|
|
}
|
|
if (should_send_keepalive(peer)) {
|
|
wireguardif_send_keepalive(device, peer);
|
|
}
|
|
if (should_send_initiation(peer)) {
|
|
wireguard_start_handshake(device->netif, peer);
|
|
}
|
|
|
|
if ((peer->curr_keypair.valid) || (peer->prev_keypair.valid)) {
|
|
link_up = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!link_up) {
|
|
// Clear the IF-UP flag on netif
|
|
netif_set_link_down(device->netif);
|
|
}
|
|
}
|
|
|
|
|
|
err_t wireguardif_init(struct netif *netif) {
|
|
err_t result;
|
|
esp_err_t err = ESP_FAIL;
|
|
struct wireguardif_init_data *init_data;
|
|
wireguard_device_t *device;
|
|
struct udp_pcb *udp;
|
|
|
|
struct netif* underlying_netif = NULL;
|
|
|
|
#ifdef ESP32
|
|
char lwip_netif_name[8] = {0,};
|
|
|
|
// list of interfaces to try to bind wireguard to
|
|
static const char* ifkeys[3] = {"ETH_DEF", "WIFI_STA_DEF", "PPP_DEF"};
|
|
|
|
// ifkey will contain the selected interface key
|
|
const char* ifkey = NULL;
|
|
|
|
// AddLog(LOG_LEVEL_DEBUG, "WG : looking for available network interface");
|
|
for (int32_t i = 0; i < sizeof(ifkeys) / sizeof(char *) && err != ESP_OK; i++) {
|
|
ifkey = ifkeys[i];
|
|
err = esp_netif_get_netif_impl_name(esp_netif_get_handle_from_ifkey(ifkey), lwip_netif_name);
|
|
if (err == ESP_OK) {
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("WG : Found available network interface: %s"), lwip_netif_name);
|
|
}
|
|
}
|
|
if (err != ESP_OK) {
|
|
AddLog(LOG_LEVEL_INFO, PSTR("WG : could not find an available network interface"));
|
|
return ERR_IF;
|
|
}
|
|
|
|
underlying_netif = netif_find(lwip_netif_name);
|
|
|
|
if (underlying_netif == NULL) {
|
|
AddLog(LOG_LEVEL_INFO, PSTR("WG : netif_find: cannot find %s (%s)"), ifkey, lwip_netif_name);
|
|
return ERR_IF;
|
|
}
|
|
#else // Now in ESP8266
|
|
underlying_netif = netif_default;
|
|
|
|
if (underlying_netif == NULL) {
|
|
AddLog(LOG_LEVEL_INFO, PSTR("WG : netif_find: cannot find default netif"));
|
|
return ERR_IF;
|
|
}
|
|
#endif // ESP32
|
|
|
|
|
|
// We need to initialise the wireguard module
|
|
wireguard_init();
|
|
|
|
if (netif && netif->state) {
|
|
|
|
// The init data is passed into the netif_add call as the 'state' - we will replace this with our private state data
|
|
init_data = (struct wireguardif_init_data *)netif->state;
|
|
|
|
// Clear out and set if function is successful
|
|
netif->state = NULL;
|
|
|
|
|
|
// size_t len = decode_base64_length((unsigned char*)init_data->private_key);
|
|
// if (len == WIREGUARD_PRIVATE_KEY_LEN) {
|
|
// len = decode_base64((unsigned char*)init_data->private_key, (unsigned char*)private_key);
|
|
|
|
udp = udp_new();
|
|
|
|
if (udp) {
|
|
result = udp_bind(udp, IP_ADDR_ANY, init_data->listen_port); // Note this listens on all interfaces! Really just want the passed netif
|
|
// AddLog(LOG_LEVEL_DEBUG, "WG : udp_bind result %i port %i", result, init_data->listen_port);
|
|
if (result == ERR_OK) {
|
|
device = (wireguard_device_t *) mem_calloc(1, sizeof(wireguard_device_t));
|
|
if (device) {
|
|
device->netif = netif;
|
|
device->underlying_netif = underlying_netif;
|
|
udp_bind_netif(udp, underlying_netif);
|
|
|
|
device->udp_pcb = udp;
|
|
// Per-wireguard netif/device setup
|
|
uint32_t t1 = wireguard_sys_now();
|
|
if (wireguard_device_init(device, init_data->private_key2)) {
|
|
uint32_t t2 = wireguard_sys_now();
|
|
// AddLog(LOG_LEVEL_DEBUG, "WG : device init took %" PRIi32 "ms", (t2-t1));
|
|
|
|
#if LWIP_CHECKSUM_CTRL_PER_NETIF
|
|
NETIF_SET_CHECKSUM_CTRL(netif, NETIF_CHECKSUM_ENABLE_ALL);
|
|
#endif
|
|
netif->state = device;
|
|
netif->name[0] = 'w';
|
|
netif->name[1] = 'g';
|
|
netif->output = wireguardif_output;
|
|
netif->linkoutput = NULL;
|
|
netif->hwaddr_len = 0;
|
|
netif->mtu = WIREGUARDIF_MTU;
|
|
// We set up no state flags here - caller should set them
|
|
// NETIF_FLAG_LINK_UP is automatically set/cleared when at least one peer is connected
|
|
netif->flags = 0;
|
|
|
|
udp_recv(udp, wireguardif_network_rx, device);
|
|
|
|
// Start a periodic timer for this wireguard device
|
|
sys_timeout(WIREGUARDIF_TIMER_MSECS, wireguardif_tmr, device);
|
|
|
|
result = ERR_OK;
|
|
} else {
|
|
mem_free(device);
|
|
device = NULL;
|
|
udp_remove(udp);
|
|
result = ERR_ARG;
|
|
}
|
|
} else {
|
|
udp_remove(udp);
|
|
result = ERR_MEM;
|
|
}
|
|
} else {
|
|
udp_remove(udp);
|
|
}
|
|
}
|
|
} else {
|
|
result = ERR_ARG;
|
|
}
|
|
fail:
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: OUT wireguardif_init err=%i", result);
|
|
return result;
|
|
}
|
|
|
|
void wireguardif_peer_init(wireguardif_peer_t *peer) {
|
|
if (peer) {
|
|
memset(peer, 0, sizeof(wireguardif_peer_t));
|
|
ip_addr_set_any(false, &peer->endpoint_ip);
|
|
peer->endport_port = WIREGUARDIF_DEFAULT_PORT;
|
|
peer->keep_alive = 0;
|
|
ip_addr_set_any(false, &peer->allowed_ip);
|
|
ip_addr_set_any(false, &peer->allowed_mask);
|
|
memset(peer->greatest_timestamp, 0, sizeof(peer->greatest_timestamp));
|
|
memset(peer->public_key2, 0, sizeof(peer->public_key2));
|
|
memset(peer->preshared_key2, 0, sizeof(peer->preshared_key2));
|
|
}
|
|
}
|
|
|
|
void wireguardif_shutdown(struct netif *netif) {
|
|
if (netif && netif->state) {
|
|
wireguard_device_t *device = (wireguard_device_t *)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;
|
|
}
|
|
}
|
|
}
|
|
|
|
void wireguardif_fini(struct netif *netif) {
|
|
if (netif && netif->state) {
|
|
free(netif->state);
|
|
netif->state = NULL;
|
|
}
|
|
}
|