Commit 20445b3f authored by jan.koester's avatar jan.koester
Browse files

test

parent f9098f64
Loading
Loading
Loading
Loading
+123 −22
Changes for src/network.cpp: 123 added lines, 22 removed lines.
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
#include <filesystem>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cerrno>
#include <cctype>
#include <cstdlib>
@@ -375,6 +376,13 @@ void NetworkManager::reloadConfigs() {
    });
}

static std::string readSysfs(const std::string &ifname, const char *attr) {
    std::ifstream f("/sys/class/net/" + ifname + "/" + attr);
    std::string v;
    if (f.is_open()) std::getline(f, v);
    return trim(v);
}

// the bond or bridge ifname is a member of now ("" if none)
static std::string currentMaster(const std::string &ifname) {
    std::error_code ec;
@@ -382,17 +390,23 @@ static std::string currentMaster(const std::string &ifname) {
    return ec ? std::string() : target.filename().string();
}

// members first (their bond/bridge needs them before it can get an
// address), then everything else
// the order interfaces are configured in: members of bonds, members of
// bridges (a bond in a bridge: after its own members, so it has their MAC),
// the bonds/bridges themselves (right after their members: when an address
// moves from a member to its bridge, the gap stays short), then the rest
static std::vector<std::string> membersFirst(const std::vector<std::string> &ifnames,
                                             const std::function<const NetworkConfig *(const std::string &)> &cfgOf) {
    std::vector<std::string> out;
    for (int pass = 0; pass < 2; ++pass)
        for (auto &i : ifnames) {
                                             const std::function<const NetworkConfig *(const std::string &)> &cfgOf,
                                             const std::function<bool(const std::string &)> &isNetDev) {
    auto rank = [&](const std::string &i) {
        const NetworkConfig *c = cfgOf(i);
            bool member = c && (!c->bond.empty() || !c->bridge.empty());
            if (member == (pass == 0)) out.push_back(i);
        }
        if (c && !c->bond.empty()) return 0;
        if (c && !c->bridge.empty()) return 1;
        return isNetDev(i) ? 2 : 3;
    };
    std::vector<std::string> out;
    for (int pass = 0; pass < 4; ++pass)
        for (auto &i : ifnames)
            if (rank(i) == pass) out.push_back(i);
    return out;
}

@@ -409,7 +423,24 @@ void NetworkManager::configureInterface(const std::string &ifname, const Network
            std::cerr << "[syslite/net] " << ifname << ": " << master << " doesn't exist (yet)" << std::endl;
            return;
        }
        if (currentMaster(ifname) != master) {
        if (currentMaster(ifname) != master && cfg.bond.empty()) {
            // a bridge takes members as they are (up): it goes up first, so
            // the new port forwards at once (no STP: no listening/learning)
            setLinkUp(master);
            // a bond in a bridge: the bridge keeps the bond's MAC for good
            // (else it takes the lowest of its ports' and changes with them),
            // so switches and ARP caches see the same address as before
            std::error_code ec;
            if (std::filesystem::exists("/sys/class/net/" + ifname + "/bonding", ec) &&
                readSysfs(master, "addr_assign_type") != "3" /* NET_ADDR_SET */)
                setLinkAddress(master, readSysfs(ifname, "address"));
            if (!setMaster(ifname, master))
                std::cerr << "[syslite/net] " << ifname << ": cannot add it to " << master << std::endl;
            else
                std::cout << "[syslite/net] " << ifname << ": member of " << master << std::endl;
            // its addresses only now: until it is a port they still work
            flushIpv4Addresses(ifname);
        } else if (currentMaster(ifname) != master) {
            flushIpv4Addresses(ifname);
            // a bond takes only members that are down
            setLinkDown(ifname);
@@ -485,7 +516,8 @@ void NetworkManager::applyAll() {

    applyNetDevs();
    auto cfgOf = [this](const std::string &i) { return configFor(i); };
    for (auto &ifname : membersFirst(enumInterfaces(), cfgOf)) {
    auto isNetDev = [this](const std::string &i) { return findNetDev(i) != nullptr; };
    for (auto &ifname : membersFirst(enumInterfaces(), cfgOf, isNetDev)) {
        if (_stopped.count(ifname)) continue;
        if (const NetworkConfig *cfg = configFor(ifname))
            configureInterface(ifname, *cfg);
@@ -496,7 +528,8 @@ void NetworkManager::applyAll() {
void NetworkManager::applyNewDevices() {
    bool changed = false;
    auto cfgOf = [this](const std::string &i) { return configFor(i); };
    for (auto &ifname : membersFirst(enumInterfaces(), cfgOf)) {
    auto isNetDev = [this](const std::string &i) { return findNetDev(i) != nullptr; };
    for (auto &ifname : membersFirst(enumInterfaces(), cfgOf, isNetDev)) {
        if (_configured.count(ifname) || _stopped.count(ifname)) continue;
        if (const NetworkConfig *cfg = configFor(ifname)) {
            configureInterface(ifname, *cfg);
@@ -530,13 +563,6 @@ bool NetworkManager::interfaceExists(const std::string &ifname) {
    return std::filesystem::exists("/sys/class/net/" + ifname, ec);
}

static std::string readSysfs(const std::string &ifname, const char *attr) {
    std::ifstream f("/sys/class/net/" + ifname + "/" + attr);
    std::string v;
    if (f.is_open()) std::getline(f, v);
    return trim(v);
}

InterfaceStatus NetworkManager::status(const std::string &ifname) {
    InterfaceStatus st;
    st.name = ifname;
@@ -589,7 +615,8 @@ bool NetworkManager::reloadInterface(const std::string &ifname, std::string *err
    reloadConfigs();
    applyNetDevs();
    auto cfgOf = [this](const std::string &i) { return configFor(i); };
    std::vector<std::string> targets = membersFirst(ifname.empty() ? enumInterfaces() : std::vector<std::string>{ifname}, cfgOf);
    auto isNetDev = [this](const std::string &i) { return findNetDev(i) != nullptr; };
    std::vector<std::string> targets = membersFirst(ifname.empty() ? enumInterfaces() : std::vector<std::string>{ifname}, cfgOf, isNetDev);
    for (auto &i : targets) {
        if (_stopped.count(i)) continue;
        const NetworkConfig *cfg = configFor(i);
@@ -809,7 +836,11 @@ bool NetworkManager::saveNetDev(const NetDevConfig &in, const std::vector<std::s
    for (auto &m : members) {
        if (!validIfname(m)) return fail("invalid interface name '" + m + "'");
        if (m == nd.ifname || m == "lo") return fail(m + " can't be a member of " + nd.ifname);
        if (findNetDev(m)) return fail(m + " is a bond or bridge itself");
        // a bond can be a port of a bridge; nothing else nests
        if (const NetDevConfig *inner = findNetDev(m))
            if (nd.kind != "bridge" || inner->kind != "bond")
                return fail(m + " is a " + inner->kind + " itself" +
                            (nd.kind == "bridge" ? " (a bridge takes bonds, not bridges)" : ""));
        for (auto &other : _netdevs) {
            if (other.ifname == nd.ifname) continue;
            auto mem = membersOf(other.ifname);
@@ -852,6 +883,30 @@ bool NetworkManager::saveNetDev(const NetDevConfig &in, const std::vector<std::s
            if (!writeNetworkFile(cfg, err)) return rollback();
        }
    }
    // A bond/bridge without addresses of its own takes over those of its
    // first new member that has some (a bond with its address becoming a
    // bridge port: the address moves to the bridge, the host stays
    // reachable under it). Its configuration: 10-<ifname>, like webctl's.
    auto ownConfigOf = [&](const std::string &ifname) -> const NetworkConfig * {
        for (auto &c : configs)
            if (c.matchName == ifname && c.bond.empty() && c.bridge.empty()) return &c;
        return nullptr;
    };
    if (!ownConfigOf(nd.ifname)) {
        const std::vector<std::string> before = membersOf(nd.ifname);
        for (auto &m : members) {
            if (std::find(before.begin(), before.end(), m) != before.end()) continue;
            const NetworkConfig *own = ownConfigOf(m);
            if (!own || own->filePath.rfind(base, 0) != 0 || (!own->dhcp && own->address.empty())) continue;
            NetworkConfig moved = *own;
            moved.name = memberConfigName(nd.ifname);
            moved.matchName = nd.ifname;
            if (!writeNetworkFile(moved, err)) return rollback();
            std::cout << "[syslite/net] " << nd.ifname << " takes over the addresses of " << m
                      << " (" << moved.name << ".network)" << std::endl;
            break;
        }
    }
    for (auto &m : members) {
        NetworkConfig cfg;
        cfg.name = memberConfigName(m);
@@ -876,11 +931,27 @@ bool NetworkManager::deleteNetDev(const std::string &ifname, std::string *err) {
    const std::string base = std::string(ConfigDir) + "/";
    if (nd->filePath.rfind(base, 0) != 0) { if (err) *err = nd->filePath + " is outside " + ConfigDir + ", not editable"; return false; }

    // the reverse of saveNetDev's takeover: with a single member, that one
    // gets the addresses back (a bridge on a bond is removed, the bond keeps
    // the host's address)
    const std::vector<std::string> members = membersOf(ifname);
    const NetworkConfig *own = nullptr;
    for (auto &c : _configs)
        if (c.matchName == ifname && c.bond.empty() && c.bridge.empty() && c.filePath.rfind(base, 0) == 0) { own = &c; break; }
    const bool giveBack = members.size() == 1 && own && (own->dhcp || !own->address.empty());
    const NetworkConfig ownCopy = own ? *own : NetworkConfig{};

    std::error_code ec;
    for (auto cfg : _configs) {
        if (cfg.filePath.rfind(base, 0) != 0) continue;
        if (cfg.bond == ifname || cfg.bridge == ifname) {
            if (cfg.name == memberConfigName(cfg.matchName)) {
            if (giveBack && cfg.matchName == members[0]) {
                NetworkConfig back = ownCopy;
                back.name = cfg.name;
                back.matchName = cfg.matchName;
                if (!writeNetworkFile(back, err)) return false;
                std::cout << "[syslite/net] " << cfg.matchName << " gets the addresses of " << ifname << " back" << std::endl;
            } else if (cfg.name == memberConfigName(cfg.matchName)) {
                std::filesystem::remove(cfg.filePath, ec);
            } else {
                cfg.bond.clear();
@@ -903,6 +974,13 @@ bool NetworkManager::deleteNetDev(const std::string &ifname, std::string *err) {
    }
    _stopped.erase(ifname);
    reloadConfigs();
    // the member that got the addresses back has them at once, not only
    // with the next reload
    if (giveBack && interfaceExists(members[0]) && !_stopped.count(members[0]))
        if (const NetworkConfig *cfg = configFor(members[0])) {
            unconfigureInterface(members[0]);
            configureInterface(members[0], *cfg);
        }
    updateResolv();
    return true;
}
@@ -1025,6 +1103,29 @@ bool NetworkManager::setMaster(const std::string &ifname, const std::string &mas
    return ok;
}

bool NetworkManager::setLinkAddress(const std::string &ifname, const std::string &mac) {
    unsigned char addr[6];
    unsigned v[6];
    if (std::sscanf(mac.c_str(), "%x:%x:%x:%x:%x:%x", &v[0], &v[1], &v[2], &v[3], &v[4], &v[5]) != 6) return false;
    for (int i = 0; i < 6; ++i) addr[i] = static_cast<unsigned char>(v[i]);
    int idx = getIfIndex(ifname);
    if (idx < 0) return false;
    int fd = netlinkOpen();
    if (fd < 0) return false;
    NlMsg msg{};
    msg.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
    msg.hdr.nlmsg_type = RTM_SETLINK;
    msg.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
    msg.hdr.nlmsg_seq = 1;
    auto *ifi = reinterpret_cast<struct ifinfomsg *>(NLMSG_DATA(&msg.hdr));
    ifi->ifi_family = AF_UNSPEC;
    ifi->ifi_index = idx;
    nlAddAttr(&msg.hdr, sizeof(msg), IFLA_ADDRESS, addr, 6);
    bool ok = netlinkSend(fd, &msg.hdr);
    close(fd);
    return ok;
}

// ─── Netlink: addresses ─────────────────────────────────────────────────────

// Calls fn for every address of interface idx (RTM_GETADDR dump).
+3 −0
Changes for src/network.h: 3 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -170,6 +170,9 @@ namespace syslite {
        bool createLink(const NetDevConfig &nd);
        bool deleteLink(const std::string &ifname);
        bool setMaster(const std::string &ifname, const std::string &master);
        // fixes ifname's MAC address ("aa:bb:cc:dd:ee:ff"; a bridge keeps it
        // instead of following its ports)
        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);