Files
roro9stack/lib/net/src/wg_config.h
T
twislaandClaude Opus 5.5 4404dd9380 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
2026-10-08 01:49:47 +02:00

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