Commit e85414a3 authored by jan.koester's avatar jan.koester
Browse files

test

parent cdedf5fe
Loading
Loading
Loading
Loading
+1 −1
Changes for CMakeLists.txt: 1 added line, 1 removed line.
Original line number Diff line number Diff line
cmake_minimum_required(VERSION 3.18)

project(syslite VERSION 0.1.0 LANGUAGES CXX)
project(syslite VERSION 1.1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
+51 −0
Changes for include/sysbus/client.h: 51 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -62,6 +62,15 @@ constexpr const char *StopInterface = "StopInterface";
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.
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
} // namespace method

namespace signal {
@@ -127,6 +136,40 @@ struct InterfaceInfo {
    std::string configAddress; // [Network] Address=
    std::string gateway;
    std::vector<std::string> dns;
    std::string kind;          // "bond", "bridge" or "" (what the device is)
    std::string master;        // the bond/bridge it is a member of now
    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.
struct NetDevInfo {
    std::string name;          // the interface, e.g. bond0
    std::string kind;          // bond or bridge
    std::string mode;
    int miimonMs = 0;
    std::string lacpRate;
    std::string hashPolicy;
    bool stp = false;
    std::string config;        // file name without ".netdev"
    std::string configPath;
    bool exists = false;       // the device is there
    std::vector<std::string> members;  // configured members
};

// Create: netdev and kind required; Edit: only what is set changes, kind
// can't. members, if set, becomes the complete member list.
struct NetDevSettings {
    std::string netdev;
    std::string kind;
    std::optional<std::string> mode;
    std::optional<int> miimonMs;
    std::optional<std::string> lacpRate;
    std::optional<std::string> hashPolicy;
    std::optional<bool> stp;
    std::optional<std::vector<std::string>> members;
};

// Create: interface is required; the configuration is named
@@ -206,6 +249,12 @@ public:
    bool reloadInterface(const std::string &name = "");       // empty: all
    bool forceReloadInterface(const std::string &name = "");  // empty: all

    // Bonds and bridges: true on success, otherwise see lastError().
    std::vector<NetDevInfo> listNetDevs();
    bool createNetDev(const NetDevSettings &settings);
    bool editNetDev(const NetDevSettings &settings);
    bool deleteNetDev(const std::string &netdev);

    // Signals: subscribe once, then waitSignal() returns the next one
    // (false on timeout or a closed connection; timeoutMs < 0 waits forever).
    bool subscribe();
@@ -232,5 +281,7 @@ private:
UnitInfo unitFromFields(const std::vector<std::pair<std::string, std::string>> &fields);
// Reads an InterfaceInfo from a GetInterface reply / ListInterfaces entry.
InterfaceInfo interfaceFromFields(const std::vector<std::pair<std::string, std::string>> &fields);
// Reads a NetDevInfo from a ListNetDevs entry.
NetDevInfo netDevFromFields(const std::vector<std::pair<std::string, std::string>> &fields);

} // namespace sysbus
+455 −8

File changed.

Preview size limit exceeded, changes collapsed.

+56 −0
Changes for src/network.h: 56 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -20,6 +20,11 @@ namespace syslite {
        std::string gateway;         // Gateway=192.168.1.1
        std::vector<std::string> dns; // DNS=8.8.8.8

        // [Network] Bond=/Bridge=: the interface is a member of that bond or
        // bridge (and gets no addresses of its own)
        std::string bond;
        std::string bridge;

        // [Link]
        bool        linkUp = true;

@@ -28,6 +33,29 @@ 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>
    struct NetDevConfig {
        // [NetDev]
        std::string ifname;          // Name=bond0
        std::string kind;            // Kind=bond | bridge

        // [Bond]
        std::string mode = "balance-rr";   // Mode=balance-rr, active-backup, balance-xor,
                                           //   broadcast, 802.3ad, balance-tlb, balance-alb
        int         miimonMs = 100;        // MIIMonitorSec= (link monitoring)
        std::string lacpRate;              // LACPTransmitRate=slow | fast (802.3ad)
        std::string hashPolicy;            // TransmitHashPolicy=layer2 | layer3+4 | layer2+3 | ...

        // [Bridge]
        bool        stp = false;           // STP=yes

        // Metadata
        std::string filePath;
        std::string name;            // file name without ".netdev"
    };

    // Runtime state of one network interface (from /sys/class/net and netlink)
    struct InterfaceStatus {
        std::string name;
@@ -36,6 +64,8 @@ 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 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
    };
@@ -104,6 +134,20 @@ namespace syslite {
        // Re-read the .network files only (nothing is applied)
        void reloadConfigs();

        // Bonds and bridges (.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.
        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();

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

        // Netlink: create a bond/bridge (RTM_NEWLINK with IFLA_LINKINFO),
        // delete a link, make ifname a member of master ("": none)
        bool createLink(const NetDevConfig &nd);
        bool deleteLink(const std::string &ifname);
        bool setMaster(const std::string &ifname, const std::string &master);
        // does the existing device match nd (kind, bond mode, STP)?
        bool linkMatches(const NetDevConfig &nd);

        // Match an interface name against a pattern (supports trailing *)
        bool matchInterface(const std::string &pattern, const std::string &ifname);
@@ -161,6 +216,7 @@ namespace syslite {
        static std::string formatIpv4(uint32_t ip);

        std::vector<NetworkConfig> _configs;
        std::vector<NetDevConfig> _netdevs;
        std::map<std::string, std::vector<std::string>> _dnsByIf; // DNS servers per interface
        std::set<std::string> _configured; // interfaces configureInterface() set up
        std::set<std::string> _stopped;    // interfaces stopped on request
+58 −0
Changes for src/sysbus/client.cpp: 58 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -72,10 +72,32 @@ InterfaceInfo interfaceFromFields(const std::vector<std::pair<std::string, std::
        else if (k == "config_address") i.configAddress = v;
        else if (k == "gateway") i.gateway = v;
        else if (k == "dns") i.dns.push_back(v);
        else if (k == "kind") i.kind = v;
        else if (k == "master") i.master = v;
        else if (k == "member_of") i.memberOf = v;
    }
    return i;
}

NetDevInfo netDevFromFields(const std::vector<std::pair<std::string, std::string>> &fields) {
    NetDevInfo n;
    for (const auto &f : fields) {
        const std::string &k = f.first, &v = f.second;
        if (k == "netdev") n.name = v;
        else if (k == "kind") n.kind = v;
        else if (k == "mode") n.mode = v;
        else if (k == "miimon_ms") n.miimonMs = std::atoi(v.c_str());
        else if (k == "lacp_rate") n.lacpRate = v;
        else if (k == "hash_policy") n.hashPolicy = v;
        else if (k == "stp") n.stp = v == "yes";
        else if (k == "config") n.config = v;
        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);
    }
    return n;
}

Client::~Client() { close(); }

bool Client::connect(const std::string &path) {
@@ -304,6 +326,42 @@ bool Client::getInterface(const std::string &name, InterfaceInfo &out) {
}

bool Client::createInterface(const InterfaceSettings &s) { return checkedCall(method::CreateInterface, settingsArgs(s)); }

static std::vector<std::pair<std::string, std::string>> netDevArgs(const NetDevSettings &s) {
    std::vector<std::pair<std::string, std::string>> a;
    a.emplace_back("netdev", s.netdev);
    if (!s.kind.empty()) a.emplace_back("kind", s.kind);
    if (s.mode) a.emplace_back("mode", *s.mode);
    if (s.miimonMs) a.emplace_back("miimon_ms", std::to_string(*s.miimonMs));
    if (s.lacpRate) a.emplace_back("lacp_rate", *s.lacpRate);
    if (s.hashPolicy) a.emplace_back("hash_policy", *s.hashPolicy);
    if (s.stp) a.emplace_back("stp", *s.stp ? "yes" : "no");
    if (s.members) {
        a.emplace_back("set_members", "yes");  // also when empty: no members
        for (auto &m : *s.members) a.emplace_back("member", m);
    }
    return a;
}

std::vector<NetDevInfo> Client::listNetDevs() {
    std::vector<NetDevInfo> out;
    Message r;
    if (!checkedCall(method::ListNetDevs, {}, &r)) return out;
    std::vector<std::pair<std::string, std::string>> cur;
    for (const auto &f : r.fields) {
        if (f.first == "netdev" && !cur.empty()) {
            out.push_back(netDevFromFields(cur));
            cur.clear();
        }
        cur.push_back(f);
    }
    if (!cur.empty()) out.push_back(netDevFromFields(cur));
    return out;
}

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::editInterface(const InterfaceSettings &s) { return checkedCall(method::EditInterface, settingsArgs(s)); }

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