Commit b8cad34a authored by Jan Koester's avatar Jan Koester
Browse files

test

parent 9852db72
Loading
Loading
Loading
Loading
+1 −0
Changes for CMakeLists.txt: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ add_executable(syslite
    src/service.cpp
    src/tty.cpp
    src/network.cpp
    src/wireguard.cpp
    src/busserver.cpp
    src/syslite.cpp
)
+58 −10
Changes for include/sysbus/client.h: 58 added lines, 10 removed lines.
Original line number Diff line number Diff line
@@ -63,14 +63,19 @@ constexpr const char *RestartInterface = "RestartInterface";
constexpr const char *ReloadInterface = "ReloadInterface";            // no interface: all
constexpr const char *ForceReloadInterface = "ForceReloadInterface";  // no interface: all

// Bonds and bridges: .netdev files in /etc/syslite/network, their members
// get member .network files (Bond=/Bridge=). Create/Edit change only the
// files (ReloadInterface without an interface creates the device and adds
// its members); Delete also removes the device. Root only.
// Bonds, bridges and WireGuard interfaces: .netdev files in
// /etc/syslite/network; members of bonds and bridges get member .network
// files (Bond=/Bridge=), a WireGuard interface has peers. Create/Edit
// change only the files (ReloadInterface without an interface creates the
// device, adds its members, configures WireGuard); Delete also removes the
// device. Root only, except ListNetDevs (which never shows private or
// preshared keys).
constexpr const char *ListNetDevs = "ListNetDevs";    // -> netdev, ... (repeated)
constexpr const char *CreateNetDev = "CreateNetDev";  // netdev, kind, ...
constexpr const char *EditNetDev = "EditNetDev";      // netdev, ...
constexpr const char *DeleteNetDev = "DeleteNetDev";  // netdev
// -> private_key, public_key, preshared_key: new keys, e.g. for a peer
constexpr const char *GenerateWireGuardKey = "GenerateWireGuardKey";
} // namespace method

namespace signal {
@@ -141,26 +146,59 @@ struct InterfaceInfo {
    std::string memberOf;      // configured: [Network] Bond= or Bridge=
};

// A bond or bridge (.netdev). For a bond: mode (balance-rr, active-backup,
// balance-xor, broadcast, 802.3ad, balance-tlb, balance-alb), MII
// monitoring interval, LACP rate (802.3ad: slow/fast), transmit hash policy
// (layer2, layer3+4, layer2+3, ...). For a bridge: STP.
// A WireGuard peer as configured, and what the kernel knows of it.
struct WireGuardPeerInfo {
    std::string name;
    std::string publicKey;
    bool hasPresharedKey = false;
    std::string endpoint;      // configured, host:port
    std::vector<std::string> allowedIps;
    int keepalive = 0;         // seconds, 0: off
    // live (the device exists and knows the peer)
    std::string currentEndpoint;
    long long lastHandshake = 0;  // unix time, 0: never
    unsigned long long rxBytes = 0, txBytes = 0;
};

// A bond, bridge or WireGuard interface (.netdev). For a bond: mode
// (balance-rr, active-backup, balance-xor, broadcast, 802.3ad, balance-tlb,
// balance-alb), MII monitoring interval, LACP rate (802.3ad: slow/fast),
// transmit hash policy (layer2, layer3+4, layer2+3, ...). For a bridge:
// STP. For WireGuard: its public key, listen port, routes for the peers'
// AllowedIPs, peers.
struct NetDevInfo {
    std::string name;          // the interface, e.g. bond0
    std::string kind;          // bond or bridge
    std::string kind;          // bond, bridge or wireguard
    std::string mode;
    int miimonMs = 0;
    std::string lacpRate;
    std::string hashPolicy;
    bool stp = false;
    std::string publicKey;
    int listenPort = 0;        // 0: the kernel picks one
    bool routes = false;
    std::vector<WireGuardPeerInfo> peers;
    std::string config;        // file name without ".netdev"
    std::string configPath;
    bool exists = false;       // the device is there
    std::vector<std::string> members;  // configured members
};

// A WireGuard peer to configure. presharedKey: unset keeps the peer's
// current one (none for a new peer), "" removes it.
struct WireGuardPeerSettings {
    std::string name;
    std::string publicKey;
    std::optional<std::string> presharedKey;
    std::string endpoint;
    std::vector<std::string> allowedIps;
    int keepalive = 0;
};

// Create: netdev and kind required; Edit: only what is set changes, kind
// can't. members, if set, becomes the complete member list.
// can't. members, if set, becomes the complete member list; peers the
// complete peer list. A new WireGuard interface without privateKey gets a
// generated one; generateKey replaces it with a new one.
struct NetDevSettings {
    std::string netdev;
    std::string kind;
@@ -170,6 +208,15 @@ struct NetDevSettings {
    std::optional<std::string> hashPolicy;
    std::optional<bool> stp;
    std::optional<std::vector<std::string>> members;
    std::optional<std::string> privateKey;
    bool generateKey = false;
    std::optional<int> listenPort;
    std::optional<bool> routes;
    std::optional<std::vector<WireGuardPeerSettings>> peers;
};

struct WireGuardKeys {
    std::string privateKey, publicKey, presharedKey;
};

// Create: interface is required; the configuration is named
@@ -254,6 +301,7 @@ public:
    bool createNetDev(const NetDevSettings &settings);
    bool editNetDev(const NetDevSettings &settings);
    bool deleteNetDev(const std::string &netdev);
    bool generateWireGuardKey(WireGuardKeys &out);

    // Signals: subscribe once, then waitSignal() returns the next one
    // (false on timeout or a closed connection; timeoutMs < 0 waits forever).
+274 −41

File changed.

Preview size limit exceeded, changes collapsed.

+39 −11
Changes for src/network.h: 39 added lines, 11 removed lines.
Original line number Diff line number Diff line
@@ -6,6 +6,8 @@
#include <set>
#include <cstdint>

#include "wireguard.h"

namespace syslite {

    // Parsed from systemd-networkd .network files
@@ -33,13 +35,14 @@ namespace syslite {
        std::string name;
    };

    // Parsed from systemd-networkd .netdev files: a bond or a bridge that
    // syslite creates; its members are the interfaces whose .network says
    // Bond=<name> / Bridge=<name>
    // Parsed from systemd-networkd .netdev files: a bond, a bridge or a
    // WireGuard interface that syslite creates; the members of a bond or
    // bridge are the interfaces whose .network says Bond=<name> /
    // Bridge=<name>; a WireGuard interface has its peers instead
    struct NetDevConfig {
        // [NetDev]
        std::string ifname;          // Name=bond0
        std::string kind;            // Kind=bond | bridge
        std::string kind;            // Kind=bond | bridge | wireguard

        // [Bond]
        std::string mode = "balance-rr";   // Mode=balance-rr, active-backup, balance-xor,
@@ -51,6 +54,15 @@ namespace syslite {
        // [Bridge]
        bool        stp = false;           // STP=yes

        // [WireGuard]
        std::string wgPrivateKey;          // PrivateKey= (base64), or
        std::string wgPrivateKeyFile;      // PrivateKeyFile=
        int         wgListenPort = 0;      // ListenPort= (0, "auto": the kernel picks one)
        uint32_t    wgFwMark = 0;          // FirewallMark=
        bool        wgRoutes = false;      // RouteTable=main: routes for the peers' AllowedIPs
        // [WireGuardPeer], one section each
        std::vector<WireGuardPeer> wgPeers;

        // Metadata
        std::string filePath;
        std::string name;            // file name without ".netdev"
@@ -64,7 +76,7 @@ namespace syslite {
        bool        adminUp = false; // IFF_UP
        bool        carrier = false;
        bool        stopped = false; // stopped on request (stays down)
        std::string kind;            // "bond", "bridge" or "" (/sys/class/net/<if>/bonding, /bridge)
        std::string kind;            // "bond", "bridge", "wireguard" or ""
        std::string master;          // the bond or bridge it is a member of
        std::vector<std::string> addresses; // "a.b.c.d/len", IPv6 too
        const NetworkConfig *config = nullptr; // first matching configuration
@@ -114,7 +126,8 @@ namespace syslite {
        //         servers; it stays down (also for new-device events) until
        //         started again
        //   reload: re-read the files and reconfigure without taking the
        //           link down (empty ifname: all interfaces)
        //           link down (empty ifname: all interfaces; a .netdev's
        //           device that isn't there yet is created)
        //   forceReload: re-read the files and stop + start (empty: all)
        bool startInterface(const std::string &ifname, std::string *err);
        bool stopInterface(const std::string &ifname, std::string *err);
@@ -134,24 +147,32 @@ namespace syslite {
        // Re-read the .network files only (nothing is applied)
        void reloadConfigs();

        // Bonds and bridges (.netdev files in ConfigDir). saveNetDev writes
        // Bonds, bridges and WireGuard interfaces (.netdev files in
        // ConfigDir). saveNetDev writes
        // ConfigDir/<nd.name>.netdev and makes exactly `members` its members:
        // each gets a member configuration ConfigDir/10-<member>.network
        // (Bond=/Bridge=), former members lose theirs. deleteNetDev removes
        // the file, the member configurations and the device itself. Both
        // re-read the files; applyNetDevs() (also part of applyAll/reload)
        // creates what is missing.
        // creates what is missing and configures the WireGuard interfaces.
        // A WireGuard .netdev holds its private key: written 0600, and
        // saveNetDev generates one if nd has none.
        const std::vector<NetDevConfig> &netDevs() const { return _netdevs; }
        const NetDevConfig *findNetDev(const std::string &ifname) const;
        std::vector<std::string> membersOf(const std::string &ifname) const;
        bool saveNetDev(const NetDevConfig &nd, const std::vector<std::string> &members, std::string *err);
        bool deleteNetDev(const std::string &ifname, std::string *err);
        void applyNetDevs();
        // a bond or bridge device without a .netdev (left over, e.g. from an
        // aborted change): the device only is deleted; false if ifname
        // isn't such a device
        // a bond, bridge or WireGuard device without a .netdev (left over,
        // e.g. from an aborted change): the device only is deleted; false
        // if ifname isn't such a device
        bool deleteUnconfiguredLink(const std::string &ifname, std::string *err);

        // A WireGuard interface's public key ("" if its private key is
        // missing) and what the kernel knows of its peers (by public key)
        std::string wireGuardPublicKey(const NetDevConfig &nd) const;
        std::map<std::string, WireGuardPeerStatus> wireGuardPeers(const std::string &ifname);

        // Start listening for new network devices (netlink RTNLGRP_LINK)
        // Returns fd to poll in the main loop (-1 on failure)
        int startDeviceMonitor();
@@ -167,6 +188,7 @@ namespace syslite {
        bool parseNetworkFile(const std::string &path, NetworkConfig &cfg);
        // ...and .netdev file
        bool parseNetDevFile(const std::string &path, NetDevConfig &nd);
        bool writeNetDevFile(const NetDevConfig &nd, std::string *err);
        bool writeNetworkFile(const NetworkConfig &cfg, std::string *err);

        // Netlink: create a bond/bridge (RTM_NEWLINK with IFLA_LINKINFO),
@@ -179,6 +201,12 @@ namespace syslite {
        bool setLinkAddress(const std::string &ifname, const std::string &mac);
        // does the existing device match nd (kind, bond mode, STP)?
        bool linkMatches(const NetDevConfig &nd);
        // WireGuard: keys, port and peers into the kernel; routes for the
        // peers' AllowedIPs (RouteTable=main; once the link is up)
        bool configureWireGuard(const NetDevConfig &nd, std::string *err);
        void addWireGuardRoutes(const NetDevConfig &nd);
        // Netlink: a route to cidr over ifname (no gateway)
        bool addPrefixRoute(const std::string &ifname, const std::string &cidr);

        // Match an interface name against a pattern (supports trailing *)
        bool matchInterface(const std::string &pattern, const std::string &ifname);
+42 −0
Changes for src/sysbus/client.cpp: 42 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -94,6 +94,24 @@ NetDevInfo netDevFromFields(const std::vector<std::pair<std::string, std::string
        else if (k == "config_path") n.configPath = v;
        else if (k == "exists") n.exists = v == "yes";
        else if (k == "member") n.members.push_back(v);
        else if (k == "public_key") n.publicKey = v;
        else if (k == "listen_port") n.listenPort = std::atoi(v.c_str());
        else if (k == "routes") n.routes = v == "yes";
        else if (k == "peer") {
            n.peers.emplace_back();
            n.peers.back().publicKey = v;
        } else if (k.compare(0, 5, "peer_") == 0 && !n.peers.empty()) {
            WireGuardPeerInfo &p = n.peers.back();
            if (k == "peer_name") p.name = v;
            else if (k == "peer_has_psk") p.hasPresharedKey = v == "yes";
            else if (k == "peer_endpoint") p.endpoint = v;
            else if (k == "peer_allowed_ip") p.allowedIps.push_back(v);
            else if (k == "peer_keepalive") p.keepalive = std::atoi(v.c_str());
            else if (k == "peer_current_endpoint") p.currentEndpoint = v;
            else if (k == "peer_handshake") p.lastHandshake = std::strtoll(v.c_str(), nullptr, 10);
            else if (k == "peer_rx") p.rxBytes = std::strtoull(v.c_str(), nullptr, 10);
            else if (k == "peer_tx") p.txBytes = std::strtoull(v.c_str(), nullptr, 10);
        }
    }
    return n;
}
@@ -340,6 +358,21 @@ static std::vector<std::pair<std::string, std::string>> netDevArgs(const NetDevS
        a.emplace_back("set_members", "yes");  // also when empty: no members
        for (auto &m : *s.members) a.emplace_back("member", m);
    }
    if (s.privateKey) a.emplace_back("private_key", *s.privateKey);
    if (s.generateKey) a.emplace_back("generate_key", "yes");
    if (s.listenPort) a.emplace_back("listen_port", std::to_string(*s.listenPort));
    if (s.routes) a.emplace_back("routes", *s.routes ? "yes" : "no");
    if (s.peers) {
        a.emplace_back("set_peers", "yes");  // also when empty: no peers
        for (auto &p : *s.peers) {
            a.emplace_back("peer", p.publicKey);  // starts the peer's fields
            if (!p.name.empty()) a.emplace_back("peer_name", p.name);
            if (p.presharedKey) a.emplace_back("peer_psk", *p.presharedKey);
            if (!p.endpoint.empty()) a.emplace_back("peer_endpoint", p.endpoint);
            for (auto &ip : p.allowedIps) a.emplace_back("peer_allowed_ip", ip);
            if (p.keepalive) a.emplace_back("peer_keepalive", std::to_string(p.keepalive));
        }
    }
    return a;
}

@@ -362,6 +395,15 @@ std::vector<NetDevInfo> Client::listNetDevs() {
bool Client::createNetDev(const NetDevSettings &s) { return checkedCall(method::CreateNetDev, netDevArgs(s)); }
bool Client::editNetDev(const NetDevSettings &s) { return checkedCall(method::EditNetDev, netDevArgs(s)); }
bool Client::deleteNetDev(const std::string &n) { return checkedCall(method::DeleteNetDev, {{"netdev", n}}); }

bool Client::generateWireGuardKey(WireGuardKeys &out) {
    Message r;
    if (!checkedCall(method::GenerateWireGuardKey, {}, &r)) return false;
    out.privateKey = r.get("private_key");
    out.publicKey = r.get("public_key");
    out.presharedKey = r.get("preshared_key");
    return true;
}
bool Client::editInterface(const InterfaceSettings &s) { return checkedCall(method::EditInterface, settingsArgs(s)); }

bool Client::deleteInterface(const std::string &interface, const std::string &config) {
Loading