Public Access
The device joins a WireGuard network over whatever Wi-Fi it is on: one peer, IPv4. A client's .conf is imported from the card (/vpn/wg0.conf) and kept in the device's settings, private key included, never shown; Settings offers to delete the file. A switch brings the tunnel up until the next restart, "Start with Wi-Fi" every time; it waits for the clock, which a handshake needs. VPN shows in the Status Bar. The protocol is esphome/wireguard 0.4.8. It calls lwIP without lwIP's lock, which this framework checks: every call into it is made with the lock held. What goes through the tunnel is everything (AllowedIPs 0.0.0.0/0) or the one subnet the device's tunnel address is in: lwIP routes by an interface's subnet or by default, nothing finer. The import says how many ranges it can't reach. Checked against a test peer in both directions and against a real server, with a configuration uploaded from a phone (docs/milestones/N1.md). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
75 lines
2.8 KiB
C++
75 lines
2.8 KiB
C++
#pragma once
|
|
|
|
#include <string>
|
|
|
|
#include "event_bus.h"
|
|
#include "service.h"
|
|
#include "services/clock_service.h"
|
|
#include "services/storage_service.h"
|
|
#include "services/wifi_service.h"
|
|
#include "settings.h"
|
|
#include "wg_config.h"
|
|
|
|
namespace roro {
|
|
|
|
// The WireGuard tunnel (issue #8, docs/milestones/N1.md): one peer, IPv4, over whatever Wi-Fi the
|
|
// device is on. The protocol is the `esphome/wireguard` library's; this decides when the tunnel
|
|
// is up, takes lwIP's lock around every call into it (the library takes none), and puts the
|
|
// tunnel's DNS servers in and out.
|
|
//
|
|
// It starts once Wi-Fi is connected and the clock is set: a handshake carries the time, and a
|
|
// server refuses one older than the last it saw from this key.
|
|
class VpnService : public Service {
|
|
public:
|
|
enum class State { NoConfig, Off, WaitingWifi, WaitingClock, Resolving, Trying, Up };
|
|
|
|
VpnService(Settings& settings, WifiService& wifi, ClockService& clock, EventBus& bus)
|
|
: settings_(settings), wifi_(wifi), clock_(clock), bus_(bus) {}
|
|
const char* name() const override { return "vpn"; }
|
|
void start() override;
|
|
void stop() override { takeDown(); }
|
|
void tick(uint32_t nowMs) override;
|
|
|
|
State state() const { return state_; }
|
|
const char* stateText() const;
|
|
bool configured() const { return configured_; }
|
|
const net::WgConfig& config() const { return config_; } // its keys are for the library only
|
|
bool wanted() const { return wanted_; }
|
|
// On or off, until the next restart; `seconds`: on for that long, then off by itself (for
|
|
// trying a configuration from afar, when a wrong one would cut the connection it was sent over).
|
|
void want(bool on, uint32_t seconds = 0);
|
|
|
|
// A `.conf`'s text, or the file itself. "" or why it wasn't taken. A tunnel that is up starts
|
|
// again with the new one.
|
|
std::string import(const std::string& confText);
|
|
std::string importFile(StorageService& storage, const std::string& path);
|
|
void forget();
|
|
|
|
bool dnsThroughIt() const; // the tunnel's DNS servers are the ones in use
|
|
int64_t lastHandshake() const { return lastHandshake_; } // UTC seconds, 0: none yet
|
|
const std::string& lastError() const { return error_; }
|
|
|
|
private:
|
|
struct Tunnel; // the library's structures, kept out of this header
|
|
|
|
void bringUp();
|
|
void takeDown();
|
|
void loadConfig();
|
|
void keepDns(); // puts the tunnel's servers back in if a DHCP renewal replaced them
|
|
|
|
Settings& settings_;
|
|
WifiService& wifi_;
|
|
ClockService& clock_;
|
|
EventBus& bus_;
|
|
net::WgConfig config_;
|
|
bool configured_ = false, wanted_ = false, announced_ = false;
|
|
State state_ = State::NoConfig;
|
|
Tunnel* tunnel_ = nullptr;
|
|
uint32_t untilMs_ = 0, retryMs_ = 0;
|
|
bool timed_ = false;
|
|
int64_t lastHandshake_ = 0;
|
|
std::string error_;
|
|
};
|
|
|
|
} // namespace roro
|