Ability to fetch information from netlink (list netdev) 05/36105/23
authorMateusz Malicki <m.malicki2@samsung.com>
Mon, 2 Mar 2015 11:44:42 +0000 (12:44 +0100)
committerMateusz Malicki <m.malicki2@samsung.com>
Tue, 24 Mar 2015 15:44:05 +0000 (16:44 +0100)
[Feature]       Ability to fetch information from netlink, list netdevs
[Cause]         N/A
[Solution]      N/A
[Verification]  Build, run test, run cli command (zone_get_netdevs)

Change-Id: I404f6c2c47b0d2882b94649c134981a09a978a52

common/netlink/netlink-message.cpp
common/netlink/netlink-message.hpp
common/netlink/netlink.cpp
common/netlink/netlink.hpp
server/netdev.cpp
server/netdev.hpp
server/zone-admin.cpp
tests/unit_tests/server/ut-zone.cpp

index 9f33450..ac42c58 100644 (file)
 
 #include <logger/logger.hpp>
 
+#include <algorithm>
 #include <memory>
 #include <cstring>
+#include <atomic>
 #include <cassert>
 
 #include <linux/netlink.h>
 #include <sys/socket.h>
 #include <unistd.h>
 
-#ifndef PAGE_SIZE
-#define PAGE_SIZE 4096
-#endif
-
 namespace {
 
-const int NLMSG_GOOD_SIZE = 2*PAGE_SIZE;
-inline rtattr* NLMSG_TAIL(nlmsghdr* nmsg)
+inline const rtattr* asAttr(const void* data) { return reinterpret_cast<const rtattr*>(data); }
+inline const nlmsghdr* asHdr(const void* data) { return reinterpret_cast<const nlmsghdr*>(data); }
+inline rtattr* asAttr(void* data) { return reinterpret_cast<rtattr*>(data); }
+inline nlmsghdr* asHdr(void* data) { return reinterpret_cast<nlmsghdr*>(data); }
+inline char* NLMSG_TAIL(nlmsghdr* nmsg)
 {
-    return reinterpret_cast<rtattr*>(reinterpret_cast<char*>(nmsg) + NLMSG_ALIGN(nmsg->nlmsg_len));
+    return reinterpret_cast<char*>(nmsg) + NLMSG_ALIGN(nmsg->nlmsg_len);
 }
 
 } // namespace
@@ -55,10 +56,15 @@ inline rtattr* NLMSG_TAIL(nlmsghdr* nmsg)
 namespace vasum {
 namespace netlink {
 
+NetlinkResponse send(const NetlinkMessage& msg)
+{
+    return send(msg, 0);
+}
+
 NetlinkMessage::NetlinkMessage(uint16_t type, uint16_t flags)
 {
-    static uint32_t seq = 0;
-    mNlmsg.resize(NLMSG_GOOD_SIZE, 0);
+    static std::atomic<uint32_t> seq(0);
+    mNlmsg.resize(NLMSG_HDRLEN, 0);
     hdr().nlmsg_len = NLMSG_HDRLEN;
     hdr().nlmsg_flags = flags | NLM_F_ACK;
     hdr().nlmsg_type = type;
@@ -68,18 +74,17 @@ NetlinkMessage::NetlinkMessage(uint16_t type, uint16_t flags)
 
 NetlinkMessage& NetlinkMessage::beginNested(int ifla)
 {
-    struct rtattr *nest = NLMSG_TAIL(&hdr());
+    auto offset = std::distance(reinterpret_cast<char*>(&hdr()), NLMSG_TAIL(&hdr()));
     put(ifla, NULL, 0);
-    mNested.push(nest);
+    mNested.push(offset);
     return *this;
 }
 
 NetlinkMessage& NetlinkMessage::endNested()
 {
     assert(!mNested.empty());
-    rtattr *nest = reinterpret_cast<rtattr*>(mNested.top());
-    nest->rta_len = std::distance(reinterpret_cast<char*>(nest),
-                                  reinterpret_cast<char*>(NLMSG_TAIL(&hdr())));
+    rtattr* nest = asAttr(reinterpret_cast<char*>(&hdr()) +  mNested.top());
+    nest->rta_len = std::distance(reinterpret_cast<char*>(nest), NLMSG_TAIL(&hdr()));
     mNested.pop();
     return *this;
 }
@@ -96,7 +101,7 @@ NetlinkMessage& NetlinkMessage::put(int ifla, const void* data, int len)
     int newLen = NLMSG_ALIGN(hdr().nlmsg_len) + RTA_ALIGN(rtalen);
 
     setMinCapacity(newLen);
-    rta = NLMSG_TAIL(&hdr());
+    rta = asAttr(NLMSG_TAIL(&hdr()));
     rta->rta_type = ifla;
     rta->rta_len = rtalen;
     memcpy(RTA_DATA(rta), data, len);
@@ -113,11 +118,11 @@ NetlinkMessage& NetlinkMessage::put(const void* data, int len)
 }
 
 nlmsghdr& NetlinkMessage::hdr() {
-    return *reinterpret_cast<nlmsghdr*>(mNlmsg.data());
+    return *asHdr(mNlmsg.data());
 }
 
 const nlmsghdr& NetlinkMessage::hdr() const {
-    return *reinterpret_cast<const nlmsghdr*>(mNlmsg.data());
+    return *asHdr(mNlmsg.data());
 }
 
 void NetlinkMessage::setMinCapacity(unsigned int size)
@@ -127,41 +132,179 @@ void NetlinkMessage::setMinCapacity(unsigned int size)
     }
 }
 
-void send(const NetlinkMessage& msg)
+NetlinkResponse::NetlinkResponse(std::unique_ptr<std::vector<char>>&& message)
+    : mNlmsg(std::move(message))
+    , mNlmsgHdr(asHdr(mNlmsg.get()->data()))
+    , mPosition(NLMSG_HDRLEN)
 {
-    //TODO: Handle messages with responses
-    assert(msg.hdr().nlmsg_flags & NLM_F_ACK);
+}
+
+bool NetlinkResponse::hasMessage() const
+{
+    unsigned int tail = size() - getHdrPosition();
+    bool hasHeader = NLMSG_OK(mNlmsgHdr, tail);
+    if (!hasHeader) {
+        return false;
+    }
+    //Check if isn't ACK message
+    return NLMSG_PAYLOAD(mNlmsgHdr,0) > sizeof(uint32_t);
+}
+
+int NetlinkResponse::getMessageType() const
+{
+    return mNlmsgHdr->nlmsg_type;
+}
+
+void NetlinkResponse::fetchNextMessage()
+{
+    if (mNlmsgHdr->nlmsg_type == NLMSG_DONE) {
+        throw VasumException("There is no next message");
+    }
+    int tail = size() - mPosition;
+    mNlmsgHdr = NLMSG_NEXT(mNlmsgHdr, tail);
+    mPosition = getHdrPosition() + NLMSG_HDRLEN;
+}
+
+bool NetlinkResponse::hasAttribute() const
+{
+    assert(mPosition >= getHdrPosition());
+    int tail = mNlmsgHdr->nlmsg_len - (mPosition - getHdrPosition());
+    return RTA_OK(asAttr(get(0)), tail);
+}
 
-    const int answerLen = NLMSG_ALIGN(msg.hdr().nlmsg_len + sizeof(nlmsgerr));
-    std::unique_ptr<char[]> answerBuff(new char[answerLen]);
-    nlmsghdr* answer = reinterpret_cast<nlmsghdr*>(answerBuff.get());
-    answer->nlmsg_len =  answerLen;
+bool NetlinkResponse::isNestedAttribute() const
+{
+    return asAttr(get(RTA_LENGTH(0)))->rta_len == RTA_LENGTH(0);
+}
+
+void NetlinkResponse::skipAttribute()
+{
+    const rtattr *rta = asAttr(get(RTA_LENGTH(0)));
+    if (size() < mPosition + RTA_ALIGN(rta->rta_len)) {
+        LOGE("Skipping out of buffer:"
+                << " to: " << mPosition + RTA_ALIGN(rta->rta_len)
+                << ", buf size: " << size());
+        throw VasumException("Skipping out of buffer");
+    }
+    seek(RTA_ALIGN(rta->rta_len));
+}
+
+NetlinkResponse& NetlinkResponse::openNested(int ifla)
+{
+    const rtattr *rta = asAttr(get(RTA_LENGTH(0)));
+    if (rta->rta_type == ifla) {
+        LOGE("Wrong attribute type, expected: " << ifla << ", got: " << rta->rta_type);
+        throw VasumException("Wrong attribute type");
+    }
+    int pos = mPosition;
+    seek(RTA_LENGTH(0));
+    mNested.push(pos);
+    return *this;
+}
+
+NetlinkResponse& NetlinkResponse::closeNested()
+{
+    assert(!mNested.empty());
+    int pos = mNested.top();
+    const rtattr *rta = asAttr(mNlmsg->data() + pos);
+    if (rta->rta_len != mPosition - pos) {
+        LOGE("There is no nested attribute end. Did you read all attributes (read: "
+                << mPosition -  pos << ", length: " << rta->rta_len);
+        throw VasumException("There is no nested attribute end");
+    }
+    mNested.pop();
+    mPosition = pos;
+    return *this;
+}
 
+NetlinkResponse& NetlinkResponse::fetch(int ifla, std::string& value, int maxSize)
+{
+    value = std::string(get(ifla, maxSize));
+    skipAttribute();
+    return *this;
+}
+
+const char* NetlinkResponse::get(int ifla, int len) const
+{
+    const rtattr *rta = asAttr(get(RTA_LENGTH(len < 0 ? 0 : len)));
+    if (rta->rta_type != ifla) {
+        LOGE("Wrong attribute type, expected:" << ifla << ", got: " << rta->rta_type);
+        throw VasumException("Wrong attribute type");
+    }
+    if (len >= 0 && rta->rta_len != RTA_LENGTH(len)) {
+        LOGE("Wrong attribute length, expected: " << rta->rta_len + ", got " << len);
+        throw VasumException("Wrong attribute length");
+    }
+    return reinterpret_cast<const char*>(RTA_DATA(get(rta->rta_len)));
+}
+
+const char* NetlinkResponse::get(int len) const
+{
+    if (size() < mPosition + len) {
+        LOGE("Read out of buffer:"
+                << " from: " << mPosition + len
+                << ", buf size: " << size());
+        throw VasumException("Read out of buffer");
+    }
+    return mNlmsg->data() + mPosition;
+}
+
+NetlinkResponse& NetlinkResponse::fetch(int ifla, char* data, int len)
+{
+    std::copy_n(get(ifla, len), len, data);
+    skipAttribute();
+    return *this;
+}
+
+NetlinkResponse& NetlinkResponse::fetch(char* data, int len)
+{
+    std::copy_n(get(len), len, data);
+    seek(len);
+    return *this;
+}
+
+int NetlinkResponse::getAttributeType() const
+{
+    return asAttr(get(RTA_LENGTH(0)))->rta_type;
+}
+
+NetlinkResponse& NetlinkResponse::seek(int len)
+{
+    if (size() < mPosition + len) {
+        throw VasumException("Skipping out of buffer");
+    }
+    mPosition += len;
+    return *this;
+}
+
+int NetlinkResponse::size() const
+{
+    return mNlmsg->size();
+}
+
+inline int NetlinkResponse::getHdrPosition() const
+{
+    return std::distance(reinterpret_cast<const char*>(mNlmsg->data()),
+                         reinterpret_cast<const char*>(mNlmsgHdr));
+}
+
+NetlinkResponse send(const NetlinkMessage& msg, int pid)
+{
+    assert(msg.hdr().nlmsg_flags & NLM_F_ACK);
+
+    std::unique_ptr<std::vector<char>> data;
     Netlink nl;
-    nl.open();
+    nl.open(pid);
     try {
         nl.send(&msg.hdr());
-        //Receive ACK Netlink Message
-        do {
-            nl.rcv(answer);
-        } while (answer->nlmsg_type == NLMSG_NOOP);
+        data = nl.rcv(msg.hdr().nlmsg_seq);
     } catch (const std::exception& ex) {
         LOGE("Sending failed (" << ex.what() << ")");
         nl.close();
         throw;
     }
     nl.close();
-    if (answer->nlmsg_type != NLMSG_ERROR) {
-        // It is not NACK/ACK message
-        throw VasumException("Sending failed ( unrecognized message type )");
-    }
-    nlmsgerr *err = reinterpret_cast<nlmsgerr*>(NLMSG_DATA(answer));
-    if (answer->nlmsg_seq != msg.hdr().nlmsg_seq) {
-        throw VasumException("Sending failed ( answer message was mismatched )");
-    }
-    if (err->error) {
-        throw VasumException("Sending failed (" + getSystemErrorMessage(-err->error) + ")");
-    }
+    return NetlinkResponse(std::move(data));
 }
 
 } // namespace netlink
index e828feb..6b84de4 100644 (file)
@@ -25,6 +25,7 @@
 #ifndef COMMON_NETLINK_NETLINK_MESSAGE_HPP
 #define COMMON_NETLINK_NETLINK_MESSAGE_HPP
 
+#include <memory>
 #include <string>
 #include <vector>
 #include <stack>
@@ -35,6 +36,9 @@
 namespace vasum {
 namespace netlink {
 
+class NetlinkResponse;
+class NetlinkMessage;
+
 /**
  *  NetlinkMessage is used to creatie a netlink messages
  */
@@ -81,21 +85,124 @@ public:
      * Send netlink message
      *
      * It is not thread safe
+     * @param msg Netlink message
+     * @param pid Process id which describes network namespace
      */
-    friend void send(const NetlinkMessage& msg);
+    friend NetlinkResponse send(const NetlinkMessage& msg, int pid);
 private:
     std::vector<char> mNlmsg;
-    std::stack<void*> mNested;
+    std::stack<int> mNested;
 
     NetlinkMessage& put(int ifla, const void* data, int len);
     NetlinkMessage& put(const void* data, int len);
     nlmsghdr& hdr();
     const nlmsghdr& hdr() const;
     void setMinCapacity(unsigned int size);
+};
+
+/**
+ *  NetlinkResponse is used to read netlink messages
+ */
+class NetlinkResponse {
+public:
+    /**
+     * Check if theres is next message in netlink response
+     */
+    bool hasMessage() const;
+
+    /**
+     * Fetch next message
+     */
+    void fetchNextMessage();
 
+    /**
+     * Get message type
+     */
+    int getMessageType() const;
 
+    /**
+     * Check if there is any attribute in message
+     */
+    bool hasAttribute() const;
+
+    /**
+     * Check if current attribute is nested
+     */
+    bool isNestedAttribute() const;
+
+    /**
+     * Skip attribute
+     */
+    void skipAttribute();
+
+    /**
+     * Start reading nested attribute
+     */
+    NetlinkResponse& openNested(int ifla);
+
+    /**
+     * End reading nested attribute
+     */
+    NetlinkResponse& closeNested();
+
+    ///@{
+    /**
+     * Fetch attribute
+     */
+    NetlinkResponse& fetch(int ifla, std::string& value, int maxSize = std::string::npos);
+    template<class T>
+    NetlinkResponse& fetch(int ifla, T& value);
+    ///@}
+
+    /**
+     * Get attributie type
+     **/
+    int getAttributeType() const;
+
+    /**
+     * Fetch data of type T
+     */
+    template<class T>
+    NetlinkResponse& fetch(T& value);
+
+    /**
+     * Skip data of type T
+     */
+    template<class T>
+    NetlinkResponse& skip();
+
+    /**
+     * Send netlink message
+     *
+     * It is not thread safe
+     * @param msg Netlink message
+     * @param pid Process id which describes network namespace
+     */
+    friend NetlinkResponse send(const NetlinkMessage& msg, int pid);
+private:
+    NetlinkResponse(std::unique_ptr<std::vector<char>>&& message);
+
+    std::unique_ptr<std::vector<char>> mNlmsg;
+    std::stack<int> mNested;
+    nlmsghdr* mNlmsgHdr;
+    int mPosition;
+
+    const char* get(int ifla, int iflasize) const;
+    const char* get(int size = 0) const;
+    NetlinkResponse& fetch(int ifla, char* data, int len);
+    NetlinkResponse& fetch(char* data, int len);
+    NetlinkResponse& seek(int len);
+    int size() const;
+    int getHdrPosition() const;
 };
 
+/**
+ * Send netlink message
+ *
+ * It is not thread safe
+ */
+NetlinkResponse send(const NetlinkMessage& msg);
+
 template<class T>
 NetlinkMessage& NetlinkMessage::put(int ifla, const T& value)
 {
@@ -110,6 +217,27 @@ NetlinkMessage& NetlinkMessage::put(const T& value)
     return put(&value, sizeof(value));
 }
 
+template<class T>
+NetlinkResponse& NetlinkResponse::fetch(int ifla, T& value)
+{
+    static_assert(std::is_pod<T>::value, "Require trivial and standard-layout");
+    return fetch(ifla, reinterpret_cast<char*>(&value), sizeof(value));
+}
+
+template<class T>
+NetlinkResponse& NetlinkResponse::fetch(T& value)
+{
+    static_assert(std::is_pod<T>::value, "Require trivial and standard-layout structure");
+    return fetch(reinterpret_cast<char*>(&value), sizeof(value));
+}
+
+template<class T>
+NetlinkResponse& NetlinkResponse::skip()
+{
+    static_assert(std::is_pod<T>::value, "Require trivial and standard-layout structure");
+    return seek(sizeof(T));
+}
+
 } // namespace netlink
 } // namespace vasum
 
index fa86b45..e5cf8a7 100644 (file)
@@ -26,6 +26,8 @@
 #include "netlink.hpp"
 #include "utils.hpp"
 #include "base-exception.hpp"
+#include "utils/make-clean.hpp"
+#include "utils/environment.hpp"
 
 #include <logger/logger.hpp>
 #include <cassert>
 #include <unistd.h>
 #include <linux/netlink.h>
 
-namespace vasum {
+#ifndef PAGE_SIZE
+#define PAGE_SIZE 4096
+#endif
+
+using namespace vasum;
 
 namespace {
 
-template<class T>
-T make_clean()
+const int NLMSG_RCV_GOOD_SIZE = 2*PAGE_SIZE;
+
+int vsm_recvmsg(int fd, struct msghdr *msg, int flags)
 {
-    static_assert(std::is_pod<T>::value, "make_clean require trivial and standard-layout");
-    T value;
-    std::fill_n(reinterpret_cast<char*>(&value), sizeof(value), 0);
-    return value;
+    int ret = recvmsg(fd, msg, flags);
+    if (ret < 0) {
+        LOGE("Can't receive message: " + getSystemErrorMessage());
+    } else if (ret == 0 && msg->msg_iov && msg->msg_iov->iov_len > 0) {
+        LOGE("Peer has performed an orderly shutdown");
+    } else if (msg->msg_flags & MSG_TRUNC) {
+        LOGE("Can't receive message: " + getSystemErrorMessage(EMSGSIZE));
+    } else if (msg->msg_flags & MSG_ERRQUEUE) {
+        LOGE("No data was received but an extended error");
+    } else if (msg->msg_flags & MSG_OOB) {
+        LOGE("Internal error (expedited or out-of-band data were received)");
+    } else if (msg->msg_flags & MSG_CTRUNC) {
+        LOGE("Some control data were discarded");
+    } else if (msg->msg_flags & MSG_EOR) {
+        LOGE("End-of-record");
+    } else {
+        // All ok
+        return ret;
+    }
+    throw VasumException("Can't receive netlink message");
+}
+
+void vsm_sendmsg(int fd, const struct msghdr *msg, int flags)
+{
+    int ret = sendmsg(fd, msg, flags);
+    if (ret < 0) {
+        LOGE("Can't send message: " << getSystemErrorMessage());
+        throw VasumException("Can't send netlink message");
+    }
 }
 
 } // namespace
 
+namespace vasum {
+namespace netlink {
+
 Netlink::Netlink() : mFd(-1)
 {
 }
@@ -58,22 +93,30 @@ Netlink::~Netlink()
     close();
 }
 
-void Netlink::open()
+void Netlink::open(int netNsPid)
 {
+    auto fdFactory = []{ return socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); };
+
     assert(mFd == -1);
-    mFd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+    if (netNsPid == 0 || netNsPid == getpid()) {
+        mFd = fdFactory();
+        if (mFd == -1) {
+            LOGE("Can't open socket: " << getSystemErrorMessage());
+        }
+    } else {
+        mFd = utils::passNemaspacedFd(netNsPid, CLONE_NEWNET, fdFactory);
+    }
     if (mFd == -1) {
-        LOGE("Can't open socket (" << getSystemErrorMessage() << ")");
         throw VasumException("Can't open netlink connection");
     }
 
-    sockaddr_nl local = make_clean<sockaddr_nl>();
+    sockaddr_nl local = utils::make_clean<sockaddr_nl>();
     local.nl_family = AF_NETLINK;
 
     if (bind(mFd, (struct sockaddr *)&local, sizeof(local)) < 0) {
         int err = errno;
         close();
-        LOGE("Can't bind to socket (" << getSystemErrorMessage(err) << ")");
+        LOGE("Can't bind to socket: " << getSystemErrorMessage(err));
         throw VasumException("Can't set up netlink connection");
     }
 }
@@ -86,70 +129,72 @@ void Netlink::close()
     }
 }
 
-void Netlink::send(const nlmsghdr *nlmsg)
+unsigned int Netlink::send(const void *nlmsg)
 {
-    msghdr msg = make_clean<msghdr>();
-    sockaddr_nl nladdr = make_clean<sockaddr_nl>();
-    iovec iov = make_clean<iovec>();
+    msghdr msg = utils::make_clean<msghdr>();
+    sockaddr_nl nladdr = utils::make_clean<sockaddr_nl>();
+    iovec iov = utils::make_clean<iovec>();
 
-    iov.iov_base = (void *)nlmsg;
-    iov.iov_len = nlmsg->nlmsg_len;
+    iov.iov_base = const_cast<void*>(nlmsg);
+    iov.iov_len = reinterpret_cast<const nlmsghdr*>(nlmsg)->nlmsg_len;
     msg.msg_name = &nladdr;
     msg.msg_namelen = sizeof(nladdr);
     msg.msg_iov = &iov;
     msg.msg_iovlen = 1;
     nladdr.nl_family = AF_NETLINK;
 
-    int ret = sendmsg(mFd, &msg, 0);
-    if (ret < 0) {
-        LOGE("Can't send message (" << getSystemErrorMessage() << ")");
-        throw VasumException("Can't send netlink message");
-    }
+    vsm_sendmsg(mFd, &msg, 0);
+    return reinterpret_cast<const nlmsghdr*>(nlmsg)->nlmsg_seq;
 }
 
-int Netlink::rcv(nlmsghdr *answer)
+std::unique_ptr<std::vector<char>> Netlink::rcv(unsigned int nlmsgSeq)
 {
-    //TODO: Handle too small buffer situation (buffer resizing)
-    msghdr msg = make_clean<msghdr>();
-    sockaddr_nl nladdr = make_clean<sockaddr_nl>();
-    iovec iov = make_clean<iovec>();
+    std::unique_ptr<std::vector<char>> buf(new std::vector<char>());
+
+    msghdr msg = utils::make_clean<msghdr>();
+    sockaddr_nl nladdr = utils::make_clean<sockaddr_nl>();
+    iovec iov = utils::make_clean<iovec>();
 
-    iov.iov_base = answer;
-    iov.iov_len = answer->nlmsg_len;
     msg.msg_name = &nladdr;
     msg.msg_namelen = sizeof(nladdr);
     msg.msg_iov = &iov;
     msg.msg_iovlen = 1;
     nladdr.nl_family = AF_NETLINK;
 
-    int ret = recvmsg(mFd, &msg, 0);
-    if (ret < 0) {
-        LOGE("Can't receive message (" + getSystemErrorMessage() + ")");
-        throw VasumException("Can't receive netlink message");
-    }
-    if (ret == 0) {
-        LOGE("Peer has performed an orderly shutdown");
-        throw VasumException("Can't receive netlink message");
-    }
-    if (msg.msg_flags & MSG_TRUNC) {
-        LOGE("Can't receive message (" + getSystemErrorMessage(EMSGSIZE) + ")");
-        throw VasumException("Can't receive netlink message");
-    }
-    if (msg.msg_flags & MSG_ERRQUEUE) {
-        LOGE("No data was received but an extended error");
-        throw VasumException("Can't receive netlink message");
-    }
-    if (msg.msg_flags & MSG_OOB) {
-        LOGE("Internal error (expedited or out-of-band data were received)");
-        throw VasumException("Can't receive netlink message");
-    }
-    if (msg.msg_flags & (MSG_EOR | MSG_CTRUNC)) {
-        assert(!"This should not happen!");
-        LOGE("Internal error (" << std::to_string(msg.msg_flags) << ")");
-        throw VasumException("Internal error while recaiving netlink message");
-    }
-
-    return ret;
+    nlmsghdr* answer;
+    nlmsghdr* lastOk = NULL;
+    size_t offset = 0;
+    do {
+        buf->resize(offset + NLMSG_RCV_GOOD_SIZE);
+        answer = reinterpret_cast<nlmsghdr*>(buf->data() + offset);
+        iov.iov_base = answer;
+        iov.iov_len = buf->size() - offset;
+        unsigned int ret = vsm_recvmsg(mFd, &msg, 0);
+        for (unsigned int len = ret; NLMSG_OK(answer, len); answer = NLMSG_NEXT(answer, len)) {
+            lastOk = answer;
+            if (answer->nlmsg_type == NLMSG_ERROR) {
+                // It is NACK/ACK message
+                nlmsgerr *err = reinterpret_cast<nlmsgerr*>(NLMSG_DATA(answer));
+                if (answer->nlmsg_seq != nlmsgSeq) {
+                    throw VasumException("Sending failed: answer message was mismatched");
+                }
+                if (err->error) {
+                    throw VasumException("Sending failed: " + getSystemErrorMessage(-err->error));
+                }
+            } else if (answer->nlmsg_type == NLMSG_OVERRUN) {
+                throw VasumException("Sending failed: data lost");
+            }
+        }
+        if (lastOk == NULL) {
+            LOGE("Something went terribly wrong. Check vsm_recvmsg function");
+            throw VasumException("Can't receive data from system");
+        }
+        offset +=  NLMSG_ALIGN(ret);
+    } while (lastOk->nlmsg_type != NLMSG_DONE && lastOk->nlmsg_flags & NLM_F_MULTI);
+
+    buf->resize(offset);
+    return buf;
 }
 
+} //namespace netlink
 } //namespace vasum
index 2e73ce8..8d33957 100644 (file)
 #ifndef COMMON_NETLINK_NETLINK_HPP
 #define COMMON_NETLINK_NETLINK_HPP
 
-#include <linux/netlink.h>
+#include <memory>
+#include <vector>
 
 namespace vasum {
+namespace netlink {
 
 /**
  * Netlink class is responsible for communicating
@@ -43,8 +45,10 @@ public:
 
     /**
      * Open connnection
+     *
+     * @param netNsPid pid which defines net namespace
      */
-    void open();
+    void open(int netNsPid = 0);
 
     /**
      * Close connection
@@ -58,8 +62,9 @@ public:
      * different instances at the same time
      *
      * @param nlmsg pointer to message
+     * @return sequence number
      */
-    void send(const nlmsghdr *nlmsg);
+    unsigned int send(const void* nlmsg);
 
     /**
      * Receive message
@@ -67,13 +72,15 @@ public:
      * It is not thread safe and even you shouldn't call this function on
      * different instances at the same time
      *
-     * @param answer pointer to answer buffer
+     * @param nlmsgSeq sequence number
+     * @return received data
      */
-    int rcv(nlmsghdr *answer);
+    std::unique_ptr<std::vector<char>> rcv(unsigned int nlmsgSeq);
 private:
     int mFd;
 };
 
+} // namesapce netlink
 } // namespace vasum
 
 #endif /* COMMON_NETLINK_NETLINK_HPP */
index b4a2d21..f8f9264 100644 (file)
@@ -25,6 +25,7 @@
 #include "config.hpp"
 #include "netdev.hpp"
 #include "netlink/netlink-message.hpp"
+#include "utils/make-clean.hpp"
 #include "utils.hpp"
 #include "exception.hpp"
 
@@ -55,15 +56,6 @@ namespace netdev {
 
 namespace {
 
-template<class T>
-T make_clean()
-{
-    static_assert(std::is_pod<T>::value, "make_clean require trivial and standard-layout");
-    T value;
-    std::fill_n(reinterpret_cast<char*>(&value), sizeof(value), 0);
-    return value;
-}
-
 string getUniqueVethName()
 {
     auto find = [](const ifaddrs* ifaddr, const string& name) -> bool {
@@ -109,7 +101,7 @@ void createPipedNetdev(const string& netdev1, const string& netdev2)
     validateNetdevName(netdev2);
 
     NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK);
-    ifinfomsg infoPeer = make_clean<ifinfomsg>();
+    ifinfomsg infoPeer = utils::make_clean<ifinfomsg>();
     infoPeer.ifi_family = AF_UNSPEC;
     infoPeer.ifi_change = 0xFFFFFFFF;
     nlm.put(infoPeer)
@@ -138,7 +130,7 @@ void attachToBridge(const string& bridge, const string& netdev)
         throw ZoneOperationException("Can't attach to bridge");
     }
 
-    struct ifreq ifr = make_clean<ifreq>();
+    struct ifreq ifr = utils::make_clean<ifreq>();
     strncpy(ifr.ifr_name, bridge.c_str(), IFNAMSIZ);
     ifr.ifr_ifindex = index;
     int err = ioctl(fd, SIOCBRADDIF, &ifr);
@@ -156,7 +148,7 @@ int setFlags(const string& name, uint32_t mask, uint32_t flags)
 {
     uint32_t index = getInterfaceIndex(name);
     NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK);
-    ifinfomsg infoPeer = make_clean<ifinfomsg>();
+    ifinfomsg infoPeer = utils::make_clean<ifinfomsg>();
     infoPeer.ifi_family = AF_UNSPEC;
     infoPeer.ifi_index = index;
     infoPeer.ifi_flags = flags;
@@ -176,7 +168,7 @@ void moveToNS(const string& netdev, pid_t pid)
 {
     uint32_t index = getInterfaceIndex(netdev);
     NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK);
-    ifinfomsg infopeer = make_clean<ifinfomsg>();
+    ifinfomsg infopeer = utils::make_clean<ifinfomsg>();
     infopeer.ifi_family = AF_UNSPEC;
     infopeer.ifi_index = index;
     nlm.put(infopeer)
@@ -191,7 +183,7 @@ void createMacvlan(const string& master, const string& slave, const macvlan_mode
 
     uint32_t index = getInterfaceIndex(master);
     NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK);
-    ifinfomsg infopeer = make_clean<ifinfomsg>();
+    ifinfomsg infopeer = utils::make_clean<ifinfomsg>();
     infopeer.ifi_family = AF_UNSPEC;
     infopeer.ifi_change = 0xFFFFFFFF;
     nlm.put(infopeer)
@@ -237,6 +229,24 @@ void movePhys(const pid_t& nsPid, const string& devId)
     moveToNS(devId, nsPid);
 }
 
+std::vector<std::string> listNetdev(const pid_t& nsPid)
+{
+    NetlinkMessage nlm(RTM_GETLINK, NLM_F_REQUEST|NLM_F_DUMP|NLM_F_ROOT);
+    ifinfomsg info = utils::make_clean<ifinfomsg>();
+    info.ifi_family = AF_PACKET;
+    nlm.put(info);
+    NetlinkResponse response = send(nlm, nsPid);
+    std::vector<std::string> interfaces;
+    while (response.hasMessage()) {
+        std::string ifName;
+        response.skip<ifinfomsg>();
+        response.fetch(IFLA_IFNAME, ifName);
+        interfaces.push_back(ifName);
+        response.fetchNextMessage();
+    }
+    return interfaces;
+}
+
 } //namespace netdev
 } //namespace vasum
 
index de761ca..b3d658a 100644 (file)
@@ -26,6 +26,7 @@
 #define SERVER_NETDEV_HPP
 
 #include <string>
+#include <vector>
 #include <linux/if_link.h>
 #include <sys/types.h>
 
@@ -38,6 +39,7 @@ void createMacvlan(const pid_t& nsPid,
                    const std::string& hostDev,
                    const macvlan_mode& mode);
 void movePhys(const pid_t& nsPid, const std::string& devId);
+std::vector<std::string> listNetdev(const pid_t& nsPid);
 
 } //namespace netdev
 } //namespace vasum
index 8005fb4..64092a8 100644 (file)
@@ -314,7 +314,7 @@ ZoneAdmin::NetdevAttrs ZoneAdmin::getNetdevAttrs(const std::string& /* netdev */
 
 std::vector<std::string> ZoneAdmin::getNetdevList()
 {
-    throw ZoneOperationException("Not implemented");
+    return netdev::listNetdev(mZone.getInitPid());
 }
 
 } // namespace vasum
index 37f65be..a8b608f 100644 (file)
 #include "utils/glib-loop.hpp"
 #include "utils/scoped-dir.hpp"
 #include "config/exception.hpp"
+#include "netdev.hpp"
 
 #include <memory>
 #include <string>
 #include <thread>
 #include <chrono>
 
-
 using namespace vasum;
 using namespace config;
 
@@ -126,5 +126,22 @@ BOOST_AUTO_TEST_CASE(DbusConnectionTest)
 
 // TODO: DbusReconnectionTest
 
+BOOST_AUTO_TEST_CASE(ListNetdevTest)
+{
+    typedef std::vector<std::string> NetdevList;
+
+    auto c = create(TEST_CONFIG_PATH);
+    c->start();
+    ensureStarted();
+    // Depending on the kernel configuration there can be lots of interfaces (f.e. sit0, ip6tnl0)
+    NetdevList netdevs = c->getNetdevList();
+    // Check if there is mandatory loopback interface
+    BOOST_CHECK(find(netdevs.begin(), netdevs.end(), "lo") != netdevs.end());
+    NetdevList hostNetdevs = netdev::listNetdev(0);
+    // Check if we get interfaces from zone net namespace
+    BOOST_CHECK(hostNetdevs != netdevs);
+
+    c->stop(false);
+}
 
 BOOST_AUTO_TEST_SUITE_END()