return index;
}
-uint32_t getInterfaceIndex(const std::string& name, pid_t pid)
+std::string getInterfaceName(uint32_t index)
+{
+ char buf[IFNAMSIZ];
+ if (::if_indextoname(index, buf) == NULL) {
+ const std::string msg = "No interface for index: " + std::to_string(index);
+ LOGE(msg);
+ throw NetworkException(msg);
+ }
+ return buf;
+}
+
+uint32_t getInterfaceIndex(pid_t pid, const std::string& name)
{
NetlinkMessage nlm(RTM_GETLINK, NLM_F_REQUEST | NLM_F_ACK);
ifinfomsg info = utils::make_clean<ifinfomsg>();
NetlinkResponse response = send(nlm, pid);
if (!response.hasMessage()) {
- const std::string msg = "Can't get interface index";
+ const std::string msg = "Can't get interface index for " + name;
LOGE(msg);
throw NetworkException(msg);
}
return info.ifi_index;
}
-void bridgeModify(const std::string& ifname, uint32_t masterIndex) {
+void bridgeModify(pid_t pid, const std::string& ifname, uint32_t masterIndex) {
NetlinkMessage nlm(RTM_SETLINK, NLM_F_REQUEST | NLM_F_ACK);
ifinfomsg info = utils::make_clean<ifinfomsg>();
info.ifi_family = AF_UNSPEC;
info.ifi_index = getInterfaceIndex(ifname);
nlm.put(info)
.put(IFLA_MASTER, masterIndex);
- send(nlm);
+ send(nlm, pid);
}
-void getAddressList(std::vector<InetAddr>& addrs, int family, const std::string& ifname, pid_t pid)
+void getAddressList(pid_t pid, std::vector<InetAddr>& addrs, int family, const std::string& ifname)
{
- uint32_t index = getInterfaceIndex(ifname, pid);
+ uint32_t index = getInterfaceIndex(pid, ifname);
NetlinkMessage nlm(RTM_GETADDR, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
ifaddrmsg infoAddr = utils::make_clean<ifaddrmsg>();
infoAddr.ifa_family = family; //test AF_PACKET to get all AF_INET* ?
response.fetchNextMessage();
}
}
+
+/*
+ * convert string-hex to byte array
+ */
+void toMacAddressArray(const std::string& s, char *buf, size_t bs)
+{
+ static const std::string hex = "0123456789ABCDEF";
+ int nibble = 0; //nibble counter, one nibble = 4 bits (or one hex digit)
+ int v = 0;
+ ::memset(buf, 0, bs);
+ for (size_t i = 0; i < s.length() && bs > 0; ++i) {
+ const char c = s.at(i);
+ size_t pos = hex.find(c);
+ if (pos == std::string::npos) { // ignore eny non-hex character
+ continue;
+ }
+ v <<= 4;
+ v |= pos; // add nibble
+ if (++nibble == 2) { // two nibbles collected (one byte)
+ *buf++ = v; // assign the byte
+ --bs; // decrease space left
+ v = 0; // reset byte and nibble counter
+ nibble = 0;
+ }
+ }
+}
+
} // anonymous namespace
return "";
}
-InetAddr::InetAddr(uint32_t f, int p, const std::string& a)
+InetAddr::InetAddr(const std::string& a, unsigned p, uint32_t f) :
+ prefix(p),
+ flags(f)
{
if (a.find(":") != std::string::npos) {
setType(InetAddrType::IPV6);
setType(InetAddrType::IPV4);
fromString(a, getAddr<in_addr>());
}
- flags = f;
- prefix = p;
}
void NetworkInterface::create(InterfaceType type,
.endNested()
.endNested()
.endNested();
- send(nlm);
+ send(nlm, mContainerPid);
}
void NetworkInterface::createBridge()
.endNested()
.endNested()
.put(IFLA_IFNAME, mIfname); //bridge name (will be created)
- send(nlm);
+ send(nlm, mContainerPid);
}
void NetworkInterface::createMacVLan(const std::string& maserif, MacVLanMode mode)
.endNested()
.put(IFLA_LINK, index) //master index
.put(IFLA_IFNAME, mIfname); //slave name (will be created)
- send(nlm);
+ send(nlm, mContainerPid);
}
void NetworkInterface::moveToContainer(pid_t pid)
info.ifi_index = getInterfaceIndex(mIfname);
nlm.put(info)
.put(IFLA_NET_NS_PID, pid);
- send(nlm);
+ send(nlm, mContainerPid);
mContainerPid = pid;
}
ifinfomsg info = utils::make_clean<ifinfomsg>();
info.ifi_family = AF_UNSPEC;
info.ifi_change = CHANGE_FLAGS_DEFAULT;
- info.ifi_index = getInterfaceIndex(mIfname, mContainerPid);
+ info.ifi_index = getInterfaceIndex(mContainerPid, mIfname);
nlm.put(info)
.put(IFLA_IFNAME, mIfname);
- send(nlm);
+ send(nlm, mContainerPid);
}
NetStatus NetworkInterface::status() const
{
- NetlinkMessage nlm(RTM_GETLINK, NLM_F_REQUEST | NLM_F_ACK);
- ifinfomsg info = utils::make_clean<ifinfomsg>();
- info.ifi_family = AF_UNSPEC;
- info.ifi_change = CHANGE_FLAGS_DEFAULT;
- nlm.put(info)
- .put(IFLA_IFNAME, mIfname);
-
- NetlinkResponse response = send(nlm, mContainerPid);
- if (!response.hasMessage()) {
- throw NetworkException("Can't get interface information");
- }
-
- response.fetch(info);
- return (info.ifi_flags & IFF_UP) != 0 ? NetStatus::UP : NetStatus::DOWN;
+ const Attrs& attrs = getAttrs();
+ NetStatus st = NetStatus::DOWN;
+ for (const Attr& a : attrs)
+ if (a.name == AttrName::FLAGS) {
+ uint32_t f = stoul(a.value);
+ if (f & IFF_UP) {
+ st = NetStatus::UP;
+ break;
+ }
+ }
+ return st;
}
void NetworkInterface::renameFrom(const std::string& oldif)
NetlinkMessage nlm(RTM_SETLINK, NLM_F_REQUEST | NLM_F_ACK);
ifinfomsg info = utils::make_clean<ifinfomsg>();
info.ifi_family = AF_UNSPEC;
- info.ifi_index = getInterfaceIndex(oldif, mContainerPid);
+ info.ifi_index = getInterfaceIndex(mContainerPid, oldif);
info.ifi_change = CHANGE_FLAGS_DEFAULT;
nlm.put(info)
void NetworkInterface::addToBridge(const std::string& bridge)
{
- bridgeModify(mIfname, getInterfaceIndex(bridge));
+ bridgeModify(mContainerPid, mIfname, getInterfaceIndex(mContainerPid, bridge));
}
void NetworkInterface::delFromBridge()
{
- bridgeModify(mIfname, 0);
+ bridgeModify(mContainerPid, mIfname, 0);
}
-
void NetworkInterface::setAttrs(const Attrs& attrs)
{
if (attrs.empty()) {
//TODO check this: NetlinkMessage nlm(RTM_SETLINK, NLM_F_REQUEST | NLM_F_ACK);
NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK);
ifinfomsg info = utils::make_clean<ifinfomsg>();
- info.ifi_index = getInterfaceIndex(mIfname, mContainerPid);
+ info.ifi_index = getInterfaceIndex(mContainerPid, mIfname);
info.ifi_family = AF_UNSPEC;
info.ifi_change = CHANGE_FLAGS_DEFAULT;
nlm.put<uint32_t>(IFLA_TXQLEN, txq);
}
if (!mac.empty()) {
- nlm.put(IFLA_ADDRESS, mac);
+ char buf[6];
+ toMacAddressArray(mac, buf, 6);
+ nlm.put(IFLA_ADDRESS, buf);
}
NetlinkResponse response = send(nlm, mContainerPid);
throw NetworkException("Can't get interface information");
}
- Attrs attrs;
response.fetch(info);
+ Attrs attrs;
attrs.push_back(Attr{AttrName::FLAGS, std::to_string(info.ifi_flags)});
attrs.push_back(Attr{AttrName::TYPE, std::to_string(info.ifi_type)});
* a few types of networks require 64-bit addresses instead.
*/
std::string mac;
- uint32_t mtu, link, txq;
+ uint32_t tmp;
int attrType = response.getAttributeType();
switch (attrType) {
case IFLA_ADDRESS: //1
attrs.push_back(Attr{AttrName::MAC, utils::toHexString(mac.c_str(), mac.size())});
break;
case IFLA_MTU: //4
- response.fetch(IFLA_MTU, mtu);
- attrs.push_back(Attr{AttrName::MTU, std::to_string(mtu)});
+ response.fetch(IFLA_MTU, tmp);
+ attrs.push_back(Attr{AttrName::MTU, std::to_string(tmp)});
break;
case IFLA_LINK://5
- response.fetch(IFLA_LINK, link);
- attrs.push_back(Attr{AttrName::LINK, std::to_string(link)});
+ response.fetch(IFLA_LINK, tmp);
+ attrs.push_back(Attr{AttrName::LINK, std::to_string(tmp)});
break;
case IFLA_TXQLEN://13
- response.fetch(IFLA_TXQLEN, txq);
- attrs.push_back(Attr{AttrName::TXQLEN, std::to_string(txq)});
+ response.fetch(IFLA_TXQLEN, tmp);
+ attrs.push_back(Attr{AttrName::TXQLEN, std::to_string(tmp)});
break;
+ case IFLA_OPERSTATE://16 (IF_OPER_DOWN,...,IF_OPER_UP)
case IFLA_BROADCAST://2 MAC broadcast
case IFLA_IFNAME: //3
case IFLA_QDISC: //6 queue discipline
case IFLA_WIRELESS: //11
case IFLA_PROTINFO: //12
case IFLA_MAP: //14
+ case IFLA_WEIGHT: //15
default:
response.skipAttribute();
break;
{
NetlinkMessage nlm(RTM_NEWADDR, NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK);
ifaddrmsg infoAddr = utils::make_clean<ifaddrmsg>();
- infoAddr.ifa_index = getInterfaceIndex(mIfname, mContainerPid);
+ infoAddr.ifa_index = getInterfaceIndex(mContainerPid, mIfname);
infoAddr.ifa_family = addr.getType() == InetAddrType::IPV4 ? AF_INET : AF_INET6;
infoAddr.ifa_prefixlen = addr.prefix;
infoAddr.ifa_flags = addr.flags;
{
NetlinkMessage nlm(RTM_DELADDR, NLM_F_REQUEST | NLM_F_ACK);
ifaddrmsg infoAddr = utils::make_clean<ifaddrmsg>();
- infoAddr.ifa_index = getInterfaceIndex(mIfname, mContainerPid);
+ infoAddr.ifa_index = getInterfaceIndex(mContainerPid, mIfname);
infoAddr.ifa_family = addr.getType() == InetAddrType::IPV4 ? AF_INET : AF_INET6;
infoAddr.ifa_prefixlen = addr.prefix;
infoAddr.ifa_flags = addr.flags;
std::vector<InetAddr> NetworkInterface::getInetAddressList() const
{
std::vector<InetAddr> addrs;
- getAddressList(addrs, AF_UNSPEC, mIfname, mContainerPid);
+ getAddressList(mContainerPid, addrs, AF_UNSPEC, mIfname);
return addrs;
}
+static rt_class_t getRoutingTableClass(const RoutingTable rt)
+{
+ switch (rt) {
+ case RoutingTable::UNSPEC:
+ return RT_TABLE_UNSPEC; //0
+ case RoutingTable::COMPAT:
+ return RT_TABLE_COMPAT; //252
+ case RoutingTable::DEFAULT:
+ return RT_TABLE_DEFAULT;//253
+ case RoutingTable::MAIN:
+ return RT_TABLE_MAIN; //254
+ case RoutingTable::LOCAL:
+ return RT_TABLE_LOCAL; //255
+ default: //all other are user tables (1...251), but I use only '1'
+ return static_cast<rt_class_t>(1);
+ }
+}
+static RoutingTable getRoutingTable(unsigned tbl)
+{
+ switch (tbl) {
+ case RT_TABLE_UNSPEC:
+ return RoutingTable::UNSPEC;
+ case RT_TABLE_COMPAT:
+ return RoutingTable::COMPAT;
+ case RT_TABLE_DEFAULT:
+ return RoutingTable::DEFAULT;
+ case RT_TABLE_MAIN:
+ return RoutingTable::MAIN;
+ case RT_TABLE_LOCAL:
+ return RoutingTable::LOCAL;
+ default:
+ return RoutingTable::USER;
+ }
+}
+
+void NetworkInterface::addRoute(const Route& route, const RoutingTable rt)
+{
+ InetAddrType type = route.dst.getType();
+ if (route.src.getType() != type) {
+ const std::string msg = "Family type must be the same";
+ LOGE(msg);
+ throw NetworkException(msg);
+ }
+ uint32_t index = getInterfaceIndex(mContainerPid, mIfname);
+ NetlinkMessage nlm(RTM_NEWROUTE, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK);
+
+ rtmsg msg = utils::make_clean<rtmsg>();
+
+ if (type == InetAddrType::IPV6) {
+ msg.rtm_family = AF_INET6;
+ } else if (type == InetAddrType::IPV4) {
+ msg.rtm_family = AF_INET;
+ }
+ msg.rtm_table = getRoutingTableClass(rt);
+ msg.rtm_protocol = RTPROT_BOOT;
+ msg.rtm_scope = RT_SCOPE_UNIVERSE;
+ msg.rtm_type = RTN_UNICAST;
+ msg.rtm_dst_len = route.dst.prefix;
+
+ nlm.put(msg);
+
+ if (type == InetAddrType::IPV6) {
+ if (route.dst.prefix == 0)
+ nlm.put(RTA_GATEWAY, route.dst.getAddr<in6_addr>());
+ else
+ nlm.put(RTA_DST, route.dst.getAddr<in6_addr>());
+
+ if (route.src.prefix == 128) {
+ nlm.put(RTA_PREFSRC, route.src.getAddr<in6_addr>());
+ }
+ } else if (type == InetAddrType::IPV4) {
+ if (route.dst.prefix == 0)
+ nlm.put(RTA_GATEWAY, route.dst.getAddr<in_addr>());
+ else
+ nlm.put(RTA_DST, route.dst.getAddr<in_addr>());
+
+ if (route.src.prefix == 32) {
+ nlm.put(RTA_PREFSRC, route.src.getAddr<in_addr>());
+ }
+ }
+
+ nlm.put(RTA_OIF, index);
+
+ send(nlm, mContainerPid);
+}
+
+void NetworkInterface::delRoute(const Route& route, const RoutingTable rt)
+{
+ InetAddrType type = route.dst.getType();
+ if (route.src.getType() != type) {
+ const std::string msg = "Family type must be the same";
+ LOGE(msg);
+ throw NetworkException(msg);
+ }
+ uint32_t index = getInterfaceIndex(mContainerPid, mIfname);
+ NetlinkMessage nlm(RTM_DELROUTE, NLM_F_REQUEST | NLM_F_ACK);
+
+ rtmsg msg = utils::make_clean<rtmsg>();
+ msg.rtm_scope = RT_SCOPE_NOWHERE;
+ msg.rtm_table = getRoutingTableClass(rt);
+ msg.rtm_dst_len = route.dst.prefix;
+ if (type == InetAddrType::IPV6) {
+ msg.rtm_family = AF_INET6;
+ } else if (type == InetAddrType::IPV4) {
+ msg.rtm_family = AF_INET;
+ }
+ nlm.put(msg);
+
+ if (type == InetAddrType::IPV6) {
+ nlm.put(RTA_DST, route.dst.getAddr<in6_addr>());
+ } else if (type == InetAddrType::IPV4) {
+ nlm.put(RTA_DST, route.dst.getAddr<in_addr>());
+ }
+ nlm.put(RTA_OIF, index);
+
+ send(nlm, mContainerPid);
+}
+
+static std::vector<Route> getRoutesImpl(pid_t pid, rt_class_t tbl, const std::string& ifname, int family)
+{
+ uint32_t searchindex = 0;
+ NetlinkMessage nlm(RTM_GETROUTE, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
+ ifinfomsg info = utils::make_clean<ifinfomsg>();
+ info.ifi_family = family;
+ info.ifi_change = CHANGE_FLAGS_DEFAULT;
+
+ nlm.put(info);
+
+ NetlinkResponse response = send(nlm, pid);
+
+ if (!ifname.empty()) {
+ searchindex = getInterfaceIndex(pid, ifname);
+ }
+
+ std::vector<Route> routes;
+ for ( ; response.hasMessage(); response.fetchNextMessage()) {
+ if (response.getMessageType() != RTM_NEWROUTE) {
+ LOGW("Not route info in response");
+ continue;
+ }
+ rtmsg rt;
+ response.fetch(rt);
+ if (tbl != RT_TABLE_UNSPEC && rt.rtm_table != tbl) {
+ continue;
+ }
+ if (rt.rtm_flags & RTM_F_CLONED) {
+ continue;
+ }
+
+ Route route;
+ uint32_t index = 0;
+ route.dst.flags = 0;
+ route.src.flags = 0;
+ route.dst.prefix = 0;
+ route.src.prefix = 0;
+ route.table = getRoutingTable(rt.rtm_table);
+
+ if (rt.rtm_family == AF_INET6) {
+ route.dst.setType(InetAddrType::IPV6);
+ route.src.setType(InetAddrType::IPV6);
+ } else if (rt.rtm_family == AF_INET) {
+ route.dst.setType(InetAddrType::IPV4);
+ route.src.setType(InetAddrType::IPV4);
+ } else {
+ const std::string msg = "Unsupported inet family";
+ LOGE(msg);
+ throw NetworkException(msg);
+ }
+
+ route.dst.prefix = rt.rtm_dst_len;
+ while (response.hasAttribute()) {
+ std::string tmpval;
+ uint32_t tmp;
+ int attrType = response.getAttributeType();
+ //int len = response.getAttributeLength();
+ switch (attrType) {
+ case RTA_DST: // 1
+ case RTA_GATEWAY:// 5
+ if (route.dst.getType() == InetAddrType::IPV6) {
+ response.fetch(attrType, route.dst.getAddr<in6_addr>());
+ } else {
+ response.fetch(attrType, route.dst.getAddr<in_addr>());
+ }
+ break;
+ case RTA_SRC: // 2
+ case RTA_PREFSRC:// 7
+ if (route.src.getType() == InetAddrType::IPV6) {
+ response.fetch(attrType, route.src.getAddr<in6_addr>());
+ route.src.prefix = 128;
+ } else {
+ response.fetch(attrType, route.src.getAddr<in_addr>());
+ route.src.prefix = 32;
+ }
+ break;
+ case RTA_OIF: //4
+ response.fetch(RTA_OIF, tmp);
+ index = tmp;
+ break;
+ case RTA_PRIORITY: // 6
+ response.fetch(RTA_PRIORITY, tmp);
+ route.metric = tmp;
+ break;
+ case RTA_TABLE: //15 extends and ovewrites rt.rtm_table
+ response.fetch(RTA_TABLE, tmp);
+ route.table = getRoutingTable(tmp);
+ break;
+
+ case RTA_CACHEINFO://12
+ response.skipAttribute();
+ break;
+ case RTA_IIF: // 3
+ case RTA_METRICS: // 8 array of struct rtattr
+ case RTA_MULTIPATH:// 9 array of struct rtnexthop
+ case RTA_FLOW: //11
+ default:
+ if (!searchindex) {
+ response.fetch(attrType, tmpval);
+ LOGW("rtAttr " << attrType << ":" <<
+ utils::toHexString(tmpval.c_str(), tmpval.size()));
+ } else {
+ response.skipAttribute();
+ }
+ break;
+ }
+ }
+ if (index == 0) continue;
+
+ if (searchindex == 0 || searchindex == index) {
+ route.ifname = getInterfaceName(index);
+ routes.push_back(route);
+ }
+ }
+ return routes;
+}
+
+std::vector<Route> NetworkInterface::getRoutes(const RoutingTable rt) const
+{
+ return getRoutesImpl(mContainerPid, getRoutingTableClass(rt), mIfname, AF_UNSPEC);
+}
+
void NetworkInterface::up()
{
Attrs attrs;
return iflist;
}
+std::vector<Route> NetworkInterface::getRoutes(pid_t initpid, const RoutingTable rt)
+{
+ rt_class_t tbl = getRoutingTableClass(rt);
+ return getRoutesImpl(initpid, tbl, "", AF_UNSPEC);
+}
+
} // namespace lxcpp
*/
class InetAddr {
public:
- InetAddr() {}
- InetAddr(uint32_t flags, int prefix, const std::string& addr);
+ InetAddr() = default;
+ InetAddr(const std::string& addr, unsigned prefix, uint32_t flags=0);
InetAddrType getType() const {
return static_cast<InetAddrType>(type);
return *(reinterpret_cast<const T*>(v));
}
+ unsigned prefix;
uint32_t flags;
- int prefix;
CONFIG_REGISTER
(
LOCAL,
USER,
};
-inline std::string toString(RoutingTable rt) {
+
+inline std::string toString(const RoutingTable rt)
+{
switch (rt) {
case RoutingTable::UNSPEC:
return "unspec";
}
}
+struct Route {
+ InetAddr dst;
+ InetAddr src;
+ unsigned metric;
+ std::string ifname;
+ RoutingTable table;
+};
+
enum class AttrName {
MAC,
FLAGS,
* operates on netlink device
*/
class NetworkInterface {
+ //TODO implement Netlink singleton per pid
public:
/**
* Create network interface object for the ifname in the container (network namespace)
- * Note: pid=0 is kernel
*/
NetworkInterface(const std::string& ifname, pid_t pid = 0) :
mIfname(ifname),
NetStatus status() const;
/**
- * Create network interface in container identified by mContainerPid
+ * Create network interface in container identified by @mContainerPid
+ *
* Equivalent to: ip link add @mIfname type @type [...]
* Create pair of virtual ethernet interfaces
* ip link add @mIfname type veth peer name @peerif
* Create psedo-ethernet interface on existing one
* ip link add @mIfname type macvlan link @peerif [mode @a mode]
*/
- void create(InterfaceType type, const std::string& peerif, MacVLanMode mode = MacVLanMode::PRIVATE);
+ void create(InterfaceType type, const std::string& peerif = "", MacVLanMode mode = MacVLanMode::PRIVATE);
/**
* Delete interface
/**
* Move interface to container
- * Equivalent to: ip link set dev @hostif netns @mContainerPid
+ * Equivalent to: ip link set dev @mIfname netns @pid
*/
- void moveToContainer(pid_t p);
+ void moveToContainer(pid_t pid);
/**
* Rename interface name
std::vector<InetAddr> getInetAddressList() const;
/**
+ * Add route to specified routing table
+ * Equivalent to: ip route add @route.dst.addr/@route.dst.prefix dev @mIfname (if route.src.prefix=0)
+ */
+ void addRoute(const Route& route, const RoutingTable rt = RoutingTable::MAIN);
+
+ /**
+ * Remove route from specified routing table
+ * Equivalent to: ip route del @route.dst.addr dev @mIfname
+ */
+ void delRoute(const Route& route, const RoutingTable rt = RoutingTable::MAIN);
+
+ /**
+ * Retrieve routing table for the interface
+ * Equivalent to: ip route show dev @mIfname table @rt
+ */
+ std::vector<Route> getRoutes(const RoutingTable rt = RoutingTable::MAIN) const;
+
+ /**
* Set interface up
- * Equivalent to: ip link set @mInface up
+ * Equivalent to: ip link set @mIfname up
*/
void up();
/**
* Set MAC address attribute
- * Equivalent to: ip link set @mIface address @macaddr
+ * Equivalent to: ip link set @mIfname address @macaddr
* @macaddr in format AA:BB:CC:DD:FF:GG
+ *
+ * Note: two lower bits of first byte (leftmost) specifies MAC address class:
+ * b1: 0=unicast, 1=broadcast
+ * b2: 0=global, 1=local
+ * in most cases should be b2=0, b1=1
+ * (see: https://en.wikipedia.org/wiki/MAC_address)
*/
void setMACAddress(const std::string& macaddr);
/**
* Set MTU attribute
- * Equivalent to: ip link set @mIface mtu @mtu
+ * Equivalent to: ip link set @mIfname mtu @mtu
*/
void setMTU(int mtu);
/**
* Set TxQ attribute
- * Equivalent to: ip link set @mIface txqueue @txlen
+ * Equivalent to: ip link set @mIfname txqueue @txlen
*/
void setTxLength(int txlen);
*/
static std::vector<std::string> getInterfaces(pid_t initpid);
+ /**
+ * Get list of routes (specified routing table)
+ * Equivalent to: ip route show table @rt
+ */
+ static std::vector<Route> getRoutes(pid_t initpid, const RoutingTable rt = RoutingTable::MAIN);
+
private:
void createVeth(const std::string& peerif);
void createBridge();
void createMacVLan(const std::string& masterif, MacVLanMode mode);
+ void modifyRoute(int cmd, const InetAddr& src, const InetAddr& dst);
+
const std::string mIfname; ///< network interface name inside zone
pid_t mContainerPid; ///< Container pid to operate on (0 means kernel)
};
#include "config.hpp"
#include "config/manager.hpp"
+#include "logger/logger.hpp"
#include "lxcpp/network-config.hpp"
+#include "lxcpp/process.hpp"
+#include "utils/execute.hpp"
#include "ut.hpp"
#include <iostream>
#include <net/if.h>
-namespace {
-
using namespace lxcpp;
-using namespace config;
+
+namespace {
struct Fixture {
Fixture() {}
} while (std::find(iflist.begin(), iflist.end(), name) != iflist.end());
return name;
}
+
+ static void sendCmd(int fd, const char *txt) {
+ if (::write(fd, txt, 2) != 2) {
+ throw std::runtime_error("pipe write error");
+ }
+ }
};
+
+int child_exec(void *_fd)
+{
+ int *fd = (int *)_fd;
+ char cmdbuf[2];
+ char cmd = '-';
+
+ ::close(fd[1]);
+ try {
+ lxcpp::NetworkInterface("lo").up();
+ for(;;) {
+ // child: waiting for parent
+ if (::read(fd[0], cmdbuf, 2) != 2) {
+ break;
+ }
+
+ cmd = cmdbuf[0];
+
+ if (cmd == '0') {
+ break;
+ } else if (cmd == 'a') {
+ const char *argv[] = {
+ "ip", "a", NULL
+ };
+ if (!utils::executeAndWait("/sbin/ip", argv)) {
+ throw std::runtime_error("ip addr failed");
+ }
+ } else if (cmd == 'r') {
+ const char *argv[] = {
+ "ip", "route", "list", NULL
+ };
+ if (!utils::executeAndWait("/sbin/ip", argv)) {
+ throw std::runtime_error("ip route failed");
+ }
+ } else if (cmd == 's') {
+ const char *argv[] = {
+ "bash", NULL
+ };
+ if (!utils::executeAndWait("/bin/bash", argv)) {
+ throw std::runtime_error("bash failed");
+ }
+ } else if (cmd == 'c') {
+ LOGW("connecting ... to be done");
+ } else {
+ continue;
+ }
+ }
+
+ //cleanup
+ ::close(fd[0]);
+ _exit(EXIT_SUCCESS);
+
+ } catch(...) {
+ _exit(EXIT_FAILURE);
+ }
+}
+
} // namespace
/*
BOOST_AUTO_TEST_CASE(NetworkListInterfaces)
{
std::vector<std::string> iflist;
- BOOST_CHECK_NO_THROW(iflist = NetworkInterface::getInterfaces(0));
-
- for (const auto& i : iflist) {
- NetworkInterface ni(i);
- std::cout << i << ": ";
- Attrs attrs;
- std::vector<InetAddr> addrs;
- BOOST_CHECK_NO_THROW(attrs = ni.getAttrs());
- for (const Attr& a : attrs) {
- std::cout << a.name << "=" << a.value;
- if (a.name == AttrName::FLAGS) {
- uint32_t f = stoul(a.value);
- std::cout << "(";
- std::cout << ((f & IFF_UP) !=0 ? "UP" : "DONW");
- std::cout << ")";
- }
- std::cout << "; ";
- }
- std::cout << std::endl;
-
- BOOST_CHECK_NO_THROW(addrs = ni.getInetAddressList());
- for (const InetAddr& a : addrs) {
- std::cout << " " << toString(a) << std::endl;
- }
+ BOOST_CHECK_NO_THROW(iflist=NetworkInterface::getInterfaces(0));
+ for (const auto& ifn : iflist) {
+ const Attrs& attrs = NetworkInterface(ifn).getAttrs();
+ BOOST_CHECK(attrs.size() > 0);
}
+
}
BOOST_AUTO_TEST_CASE(NetworkConfigSerialization)
cfg.addInterfaceConfig("host-veth1", "zone-eth1", InterfaceType::BRIDGE);
cfg.addInterfaceConfig("host-veth2", "zone-eth2", InterfaceType::MACVLAN);
- InetAddr addr(0, 24, "1.2.3.4");
- cfg.addInetConfig("zone-eth0", addr);
+ cfg.addInetConfig("zone-eth0", InetAddr("1.2.3.4", 24));
config::saveToJsonFile(tmpConfigFile, cfg);
BOOST_CHECK(ni1.getMode() == ni2.getMode());
}
}
+
BOOST_AUTO_TEST_CASE(NetworkBridgeCreateDestroy)
{
- std::string name = getUniqueName("lolo");
+ std::string name = getUniqueName("test-br");
NetworkInterface ni(name);
- InetAddr myip(0, 32, "10.100.1.1");
+ InetAddr myip("10.100.1.1", 32);
- BOOST_CHECK_NO_THROW(ni.create(InterfaceType::BRIDGE, ""));
- BOOST_CHECK_NO_THROW(ni.addInetAddr(myip);)
+ BOOST_CHECK_NO_THROW(ni.create(InterfaceType::BRIDGE));
+ ni.setMACAddress("12:22:33:44:55:66"); // note bit0=0 within first byte !!!
+ BOOST_CHECK_NO_THROW(ni.addInetAddr(myip));
std::vector<std::string> iflist = NetworkInterface::getInterfaces(0);
BOOST_CHECK(std::find(iflist.begin(), iflist.end(), name) != iflist.end());
std::vector<InetAddr> addrs = ni.getInetAddressList();
BOOST_CHECK(std::find(addrs.begin(), addrs.end(), myip) != addrs.end());
- for (const auto& i : iflist) {
- std::cout << " " << i;
- }
- std::cout << std::endl;
- for (const InetAddr& a : addrs) {
- std::cout << " " << toString(a) << std::endl;
- }
BOOST_CHECK_NO_THROW(ni.delInetAddr(myip));
BOOST_CHECK_NO_THROW(ni.destroy());
+ iflist = NetworkInterface::getInterfaces(0);
+ BOOST_CHECK(std::find(iflist.begin(), iflist.end(), ni.getName()) == iflist.end());
}
BOOST_AUTO_TEST_CASE(NetworkMacVLanCreateDestroy)
std::string masterif;
std::vector<std::string> iflist = NetworkInterface::getInterfaces(0);
for (const auto& ifn : iflist) {
- if (ifn == "lo") continue;
+ if (ifn == "lo") {
+ continue;
+ }
NetworkInterface n(ifn);
if (n.status() == NetStatus::UP) {
masterif = ifn;
}
}
- NetworkInterface ni(getUniqueName("lolo"));
- std::cout << " creating MACVLAN on " << masterif << std::endl;
+ NetworkInterface ni(getUniqueName("test-vlan"));
+ // creating MACVLAN on masterif
BOOST_CHECK_NO_THROW(ni.create(InterfaceType::MACVLAN, masterif, MacVLanMode::VEPA));
iflist = NetworkInterface::getInterfaces(0);
BOOST_CHECK(std::find(iflist.begin(), iflist.end(), ni.getName()) != iflist.end());
+ // destroy MACVLAN
BOOST_CHECK_NO_THROW(ni.destroy());
+
+ iflist = NetworkInterface::getInterfaces(0);
+ BOOST_CHECK(std::find(iflist.begin(), iflist.end(), ni.getName()) == iflist.end());
+}
+
+BOOST_AUTO_TEST_CASE(NetworkListRoutes)
+{
+ unsigned mainLo = 0;
+ std::vector<Route> routes;
+ // tbl MAIN, all devs
+ BOOST_CHECK_NO_THROW(routes = NetworkInterface::getRoutes(0));
+ for (auto route : routes) {
+ if (route.ifname == "lo") {
+ ++mainLo;
+ }
+ }
+
+ // tbl LOCAL, all devs
+ BOOST_CHECK_NO_THROW(routes = NetworkInterface::getRoutes(0,RoutingTable::LOCAL));
+
+ // tbl DEFAULT, all devs
+ BOOST_CHECK_NO_THROW(routes = NetworkInterface::getRoutes(0,RoutingTable::DEFAULT));
+
+ NetworkInterface ni("lo");
+ // tbl MAIN, dev lo
+ BOOST_CHECK_NO_THROW(routes = ni.getRoutes());
+ BOOST_CHECK(routes.size() == mainLo);
+
+ // tbl LOCAL, dev lo
+ BOOST_CHECK_NO_THROW(routes = ni.getRoutes(RoutingTable::LOCAL));
+}
+
+BOOST_AUTO_TEST_CASE(NetworkAddDelRoute)
+{
+ std::vector<Route> routes;
+
+ Route route = {
+ InetAddr("10.100.1.0", 24),//dst - destination network
+ InetAddr("", 0), //src - not specified (prefix=0)
+ 0, // metric
+ "", // ifname (used only when read routes)
+ RoutingTable::UNSPEC // table (used only when read rotes)
+ };
+
+ NetworkInterface ni("lo");
+
+ BOOST_CHECK_NO_THROW(ni.addRoute(route));
+ BOOST_CHECK_NO_THROW(routes = ni.getRoutes());
+ BOOST_CHECK(std::find_if(routes.begin(), routes.end(),
+ [&route](const Route& item) -> bool {
+ return item.dst == route.dst;
+ }
+ ) != routes.end()
+ );
+
+ BOOST_CHECK_NO_THROW(ni.delRoute(route));
+ BOOST_CHECK_NO_THROW(routes = ni.getRoutes());
+ BOOST_CHECK(std::find_if(routes.begin(), routes.end(),
+ [&route](const Route& item) -> bool {
+ return item.dst == route.dst;
+ }
+ ) == routes.end()
+ );
+}
+
+BOOST_AUTO_TEST_CASE(NetworkNamespaceCreate)
+{
+ int fd[2];
+ int r = ::pipe(fd);
+ BOOST_CHECK(r != -1);
+
+ pid_t pid = lxcpp::clone(child_exec, fd, CLONE_NEWNET);
+ ::close(fd[0]);
+
+ //directives for child process
+ sendCmd(fd[1], "0"); // exit
+
+ // waiting for child to finish
+ int status;
+ BOOST_CHECK_NO_THROW(status = lxcpp::waitpid(pid));
+
+ ::close(fd[1]);
+ BOOST_CHECK_MESSAGE(status == 0, "child failed");
+}
+
+// this test case shows how to create container with network
+// Note: this test needs some preparation to successfuly connect an external site:
+// 1. allow network forwading (echo 1 > /proc/sys/net/ipv4/ip_forward)
+// 2. configure ip masquarading (iptables -t nat -A POSTROUTING -s 10.0.0.0/16 ! -d 10.0.0.0/16 -j MASQUERADE)
+BOOST_AUTO_TEST_CASE(NetworkNamespaceVETH)
+{
+ const char *vbr = "vbr";
+ const char *veth1 = "veth-ma";
+ const char *veth2 = "veth-sl";
+
+ int fd[2];
+ int r = ::pipe(fd);
+ BOOST_CHECK(r != -1);
+
+ pid_t pid = lxcpp::clone(child_exec, fd, CLONE_NEWNET);
+ ::close(fd[0]);
+
+ NetworkInterface br(vbr);
+ NetworkInterface v1(veth1);
+ NetworkInterface v2(veth2);
+
+ NetworkInterface("lo", pid).up();
+
+ // creating Bridge vbr
+ BOOST_CHECK_NO_THROW(br.create(InterfaceType::BRIDGE));
+ BOOST_CHECK_NO_THROW(br.up());
+ br.addInetAddr(InetAddr("10.0.0.1", 24));
+
+ // creating VETH pair veth1 <-> veth2
+ BOOST_CHECK_NO_THROW(v1.create(InterfaceType::VETH, v2.getName()));
+
+ // add veth1 to bridge
+ BOOST_CHECK_NO_THROW(v1.addToBridge(br.getName()));
+
+ // move veth2 to network namespace (container)
+ BOOST_CHECK_NO_THROW(v2.moveToContainer(pid));
+ v2.up();
+ v2.addInetAddr(InetAddr("10.0.0.2", 24));
+
+ v1.up(); // after v2 up and configured
+
+ // add default route
+ v2.addRoute(Route{
+ InetAddr("10.0.0.1", 0), //dst - gateway
+ InetAddr("", 0), //src - not specified (prefix=0)
+ 0,
+ "",
+ RoutingTable::UNSPEC
+ });
+
+ //directives for child process
+ sendCmd(fd[1], "a"); // ip addr show
+ sendCmd(fd[1], "r"); // ip route list
+ sendCmd(fd[1], "c"); // connect extern (needs configured NAT)
+ //sendCmd(fd[1], "s"); // exec shell
+ sendCmd(fd[1], "0"); // exit
+
+ // waiting for child to finish
+ int status;
+ BOOST_CHECK_NO_THROW(status = lxcpp::waitpid(pid));
+ ::close(fd[1]);
+ BOOST_CHECK_MESSAGE(status == 0, "child failed");
+
+ BOOST_CHECK_NO_THROW(br.destroy());
}
BOOST_AUTO_TEST_SUITE_END()