Commit 8cbf51cb authored by jan.koester's avatar jan.koester
Browse files

test

parent 8ebb5e45
Loading
Loading
Loading
Loading
+8 −1
Changes for include/containerd/api.h: 8 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -91,6 +91,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *               or "dhcp": containerd asks the bridge's DHCP server, keeps
 *               the lease renewed, takes router and name servers from it
 *   gateway     its default route (not with dhcp)
 *   vlan        1-4094 (network = bridge; 0: none): the veth is an access
 *               port of that VLAN -- containerd turns on VLAN filtering of
 *               the bridge (its other ports stay untagged in VLAN 1), gives
 *               the veth the VLAN as PVID, untagged, and nothing else, and
 *               adds it tagged to the bridge's uplinks (its ports that are
 *               no veth and no VLAN device); the switch port must carry it
 *               tagged
 *   mac         eth0's MAC address; default: 02:.. made from the name,
 *               the same at every start (for DHCP reservations)
 *   dns         repeated: name servers (default: the host's)
@@ -109,7 +116,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *   ListContainers -> per container: container (name), image, state
 *       (stopped|starting|running|stopping|exited|failed), pid (host PID of
 *       its PID 1), started, exit_code, restarts, error?, network,
 *       address?, veth? (the host's end of its veth pair, in the
 *       address?, vlan?, veth? (the host's end of its veth pair, in the
 *       bridge), lease? (the address DHCP gave it), autostart, restart,
 *       memory (current bytes), tasks
 *   GetContainer {name} -> container (name), state & co. as in the list,
+1 −0
Changes for src/containers.cpp: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -409,6 +409,7 @@ void addRecord(Result &out, const std::string &name, const Settings &s) {
    out.add("network", s.network);
    if (s.network == "bridge") {
        out.add("address", s.address);
        if (s.vlan) out.add("vlan", (long long)s.vlan);
        out.add("veth", hostVeth(name));  // its end on the host, while it runs
    }
    if (isDhcp(s) && active(r)) {
+44 −0
Changes for src/netlink.cpp: 44 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <arpa/inet.h>
#include <linux/if.h>
#include <linux/if_bridge.h>
#include <linux/if_link.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
@@ -195,6 +196,49 @@ std::string deleteLink(const std::string &ifname) {
    return talk(m);
}

std::string setVlanFiltering(const std::string &bridge) {
    std::string err = link(bridge, [](Msg &m, ifinfomsg *) {
        size_t info = m.begin(IFLA_LINKINFO);
        m.str(IFLA_INFO_KIND, "bridge");
        size_t data = m.begin(IFLA_INFO_DATA);
        uint8_t on = 1;
        m.attr(IFLA_BR_VLAN_FILTERING, &on, 1);
        m.end(data);
        m.end(info);
    });
    if (err.find(strerror(EOPNOTSUPP)) != std::string::npos)
        err += " (the kernel has no bridge VLAN filtering: CONFIG_BRIDGE_VLAN_FILTERING)";
    return err.empty() ? "" : "VLAN filtering on " + err;
}

namespace {

// RTM_SETLINK/RTM_DELLINK of family AF_BRIDGE: a VLAN of a bridge port
std::string bridgeVlan(const std::string &port, int vid, bool add, uint16_t flags) {
    int i = index(port);
    if (!i) return noSuch(port);
    Msg m(add ? RTM_SETLINK : RTM_DELLINK, 0);
    auto *ifi = m.fixed<ifinfomsg>();
    ifi->ifi_family = AF_BRIDGE;
    ifi->ifi_index = i;
    size_t spec = m.begin(IFLA_AF_SPEC);
    bridge_vlan_info info{};
    info.flags = flags;
    info.vid = (uint16_t)vid;
    m.attr(IFLA_BRIDGE_VLAN_INFO, &info, sizeof(info));
    m.end(spec);
    std::string err = talk(m);
    return err.empty() ? "" : port + ": VLAN " + std::to_string(vid) + ": " + err;
}

} // namespace

std::string addBridgeVlan(const std::string &port, int vid, bool pvid) {
    return bridgeVlan(port, vid, true, pvid ? BRIDGE_VLAN_INFO_PVID | BRIDGE_VLAN_INFO_UNTAGGED : 0);
}

std::string delBridgeVlan(const std::string &port, int vid) { return bridgeVlan(port, vid, false, 0); }

namespace {

std::string address(const std::string &ifname, const std::string &cidr, bool add) {
+11 −2
Changes for src/netlink.h: 11 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -26,8 +26,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/

/* What container networking needs from rtnetlink: a veth pair, its host
 * end in a bridge, the other end moved into the container's network
 * namespace and set up there. Each function works in the network
 * end in a bridge (optionally as an access port of a VLAN of the bridge),
 * the other end moved into the container's network namespace and set up
 * there. Each function works in the network
 * namespace of the calling process and returns "" or what went wrong. */

#pragma once
@@ -51,4 +52,12 @@ std::string delAddress(const std::string &ifname, const std::string &cidr);
std::string addDefaultRoute(const std::string &gateway);
std::string deleteLink(const std::string &ifname);

// Bridge VLAN filtering (802.1Q-aware bridge): turned on for bridge (all
// ports stay untagged members of its default PVID, usually 1, so nothing
// changes for them); vid added to port -- pvid: as its PVID, untagged on
// egress (an access port), else tagged (a trunk); vid taken from port
std::string setVlanFiltering(const std::string &bridge);
std::string addBridgeVlan(const std::string &port, int vid, bool pvid);
std::string delBridgeVlan(const std::string &port, int vid);

} // namespace containerd::nl
+66 −1
Changes for src/runtime.cpp: 66 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -37,13 +37,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <map>
#include <set>

#include <dirent.h>
#include <fcntl.h>
#include <net/if.h>
#include <poll.h>
#include <grp.h>
#include <linux/capability.h>
#include <linux/ethtool.h>
#include <linux/sockios.h>
#include <sched.h>
#include <sys/mount.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/sysmacros.h>
@@ -573,6 +579,62 @@ void finish(const Launch &l, int status, const std::string &line, const std::str
    writeAll(status, line + "\n");
}

// the driver of a network device as ethtool names it ("" if unknown)
std::string linkDriver(const std::string &ifname) {
    int fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
    if (fd < 0) return "";
    ethtool_drvinfo info{};
    info.cmd = ETHTOOL_GDRVINFO;
    ifreq ifr{};
    strncpy(ifr.ifr_name, ifname.c_str(), IFNAMSIZ - 1);
    ifr.ifr_data = (char *)&info;
    int r = ioctl(fd, SIOCETHTOOL, &ifr);
    close(fd);
    return r == 0 ? std::string(info.driver) : "";
}

// the bridge's uplinks: its ports that lead out -- not the veths of
// containers, not the taps of VMs, not VLAN devices (their frames are
// tagged by the device already)
std::vector<std::string> uplinksOf(const std::string &bridge) {
    std::vector<std::string> out;
    DIR *d = opendir(("/sys/class/net/" + bridge + "/brif").c_str());
    if (!d) return out;
    while (dirent *e = readdir(d)) {
        std::string port = e->d_name, uevent;
        if (port[0] == '.') continue;
        readFile("/sys/class/net/" + port + "/uevent", uevent);
        std::string drv = linkDriver(port);
        if (drv == "veth" || drv == "tun" || uevent.find("DEVTYPE=vlan") != std::string::npos) continue;
        out.push_back(port);
    }
    closedir(d);
    return out;
}

// VLAN=: veth, a port of the bridge, becomes an access port of vid (its
// PVID, untagged, in no other VLAN); the bridge filters VLANs, its uplinks
// carry vid tagged. The uplinks keep it when the container stops: another
// one may use it, and a VLAN the switch doesn't send costs nothing.
std::string vlanAccessPort(const std::string &bridge, const std::string &veth, int vid) {
    std::string on;
    readFile("/sys/class/net/" + bridge + "/bridge/vlan_filtering", on);
    if (trim(on) != "1") {
        std::string err = nl::setVlanFiltering(bridge);
        if (!err.empty()) return err;
    }
    std::string err = nl::addBridgeVlan(veth, vid, true);
    if (!err.empty()) return err;
    // the bridge's default PVID, which every new port is a member of
    std::string def;
    readFile("/sys/class/net/" + bridge + "/bridge/default_pvid", def);
    int defPvid = trim(def).empty() ? 1 : atoi(trim(def).c_str());
    if (defPvid && defPvid != vid && !(err = nl::delBridgeVlan(veth, defPvid)).empty()) return err;
    for (auto &up : uplinksOf(bridge))
        if (!(err = nl::addBridgeVlan(up, vid, false)).empty()) return err;
    return "";
}

// a file the container has bind-mounted: rewritten in place (a rename
// would leave the container with the old one)
void rewrite(const std::string &path, const std::string &text) {
@@ -702,7 +764,10 @@ int supervise(const Launch &l, int status) {
    if (s.network == "bridge") {
        veth = hostVeth(name);
        nl::deleteLink(veth);  // left from a crash
        for (auto e : {nl::createVeth(veth, peer, macOf(s), hostMacOf(s)), nl::setMaster(veth, s.bridge), nl::setUp(veth)})
        std::string e = nl::createVeth(veth, peer, macOf(s), hostMacOf(s));
        if (e.empty()) e = nl::setMaster(veth, s.bridge);
        if (e.empty() && s.vlan) e = vlanAccessPort(s.bridge, veth, s.vlan);
        if (e.empty()) e = nl::setUp(veth);
        if (!e.empty()) {
            nl::deleteLink(veth);
            return failed(e);
Loading