Public Access
VPN: a WireGuard tunnel (#8)
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
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
// A WireGuard tunnel's configuration (issue #8, N1 Q243-Q253): read from the standard `.conf` a
|
||||
// server's owner hands out, checked, and written back in a tidy form for the device's settings.
|
||||
// One peer, IPv4. The keys are never put in a message: errors name the line and the field.
|
||||
namespace roro::net {
|
||||
|
||||
struct WgRange {
|
||||
uint32_t address = 0;
|
||||
int prefix = 0;
|
||||
};
|
||||
|
||||
struct WgConfig {
|
||||
static constexpr int kMaxRanges = 4;
|
||||
|
||||
std::string privateKey, peerKey, presharedKey; // base64, as in the file; the last may be empty
|
||||
uint32_t address = 0; // the tunnel's address on this device
|
||||
int prefix = 32;
|
||||
uint32_t dns[2] = {0, 0};
|
||||
int mtu = 0; // 0: WireGuard's 1420
|
||||
uint16_t listenPort = 0; // 0: any; a fixed one lets the peer be the one that calls
|
||||
std::string endpointHost;
|
||||
uint16_t endpointPort = 51820;
|
||||
WgRange allowed[kMaxRanges];
|
||||
int allowedCount = 0;
|
||||
int keepalive = 25; // seconds; what the file says, or 25: this device is always behind a NAT
|
||||
};
|
||||
|
||||
// "" and `out` filled, or why the file can't be used ("line 7: ...").
|
||||
std::string parseWgConf(const std::string& text, WgConfig& out);
|
||||
// The same configuration as a `.conf` again: what the settings keep.
|
||||
std::string toWgConf(const WgConfig& config);
|
||||
bool validWgKey(const std::string& key); // 32 bytes in base64
|
||||
|
||||
// What can go through the tunnel. The network stack routes by an interface's own subnet or by
|
||||
// default, nothing finer: so either everything goes through it (AllowedIPs has 0.0.0.0/0), or the
|
||||
// one subnet this device's tunnel address is in. Ranges that are neither can't be reached, and
|
||||
// the user is told how many.
|
||||
struct WgRouting {
|
||||
bool full = false; // the tunnel is the default route
|
||||
int prefix = 32; // of the tunnel interface, when not full
|
||||
int unreachable = 0; // allowed ranges outside it
|
||||
};
|
||||
WgRouting routingOf(const WgConfig& config);
|
||||
bool wgReaches(const WgConfig& config, uint32_t address); // would a packet to this address go through it?
|
||||
|
||||
// For the screen and the console: never a key.
|
||||
std::string describeWgRouting(const WgConfig& config);
|
||||
|
||||
} // namespace roro::net
|
||||
Reference in New Issue
Block a user