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
54 lines
2.2 KiB
C++
54 lines
2.2 KiB
C++
#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
|