--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * @file nether_tests.cpp
+ * @author Piotr Sawicki (p.sawicki2@partner.samsung.com)
+ * @version 1.0
+ * @brief Tests for Nether service
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <linux/netlink.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <condition_variable>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <scoped_installer.h>
+#include <sm_commons.h>
+#include <tests_common.h>
+#include <dpl/test/safe_cleanup.h>
+
+
+using namespace SecurityManagerTest;
+using namespace DPL::Test;
+
+const std::string INTERNET_ACCESS_PRIVILEGE = "http://tizen.org/privilege/internet";
+const std::string NETHER_NETNS_NAME_NONE = "";
+const std::string NETHER_NETNS_NAME_TEST = "nether_test_network_ns";
+const std::string NETNS_RUN_DIR = "/var/run/netns"; // taken from iproute2
+const std::string NETHER_NETNS_SETUP_COMMAND = "/usr/bin/setup-nether-tests-nns.sh " + NETHER_NETNS_NAME_TEST;
+const std::string NETHER_NETNS_TEARDOWN_COMMAND = "/usr/bin/teardown-nether-tests-nns.sh " + NETHER_NETNS_NAME_TEST;
+const std::string NETHER_DNS_SETUP_COMMAND = "/usr/bin/setup-nether-tests-dns.sh";
+const std::string NETHER_DNS_TEARDOWN_COMMAND = "/usr/bin/teardown-nether-tests-dns.sh";
+
+
+const ssize_t NET_BUFFER_SIZE = 2048;
+const int UDP_MESSAGES_COUNT = 20000;
+const int TCP_MESSAGES_COUNT = 20000;
+
+const uint16_t UDP_TEST_PORT = 12000;
+const uint16_t TCP_TEST_PORT = 12000;
+
+const std::string REMOTE_INTERFACE_ADDRESS = "10.1.0.2";
+const std::string REMOTE_INTERFACE_NAME = "veth1";
+const std::string LOCAL_HOST_TEST_SERVER_ADDRESS = "127.0.0.1";
+const std::string LOCAL_TEST_MCAST_GROUP = "225.0.0.250";
+const std::string DNS_TEST_ADDRESS = "www.samsung.com";
+const std::string ANY_INTERFACE = "";
+
+const char TTL_MCAST_RESTRIC_SUBNET = 1;
+
+const int MONITOR_TIMEOUT = 1000; // ms
+
+enum class NetherInternetAccess {
+ ACCESS_GRANTED,
+ ACCESS_DENIED
+};
+
+
+void runShellScriptInChildAndWait(const std::string &command)
+{
+ RUNNER_ASSERT_MSG(system(command.c_str()) != -1, "Couldn't run command: " << command);
+}
+
+
+class ScopedShellScriptRunner final {
+public:
+ ScopedShellScriptRunner(const std::string &setupCmd, const std::string &teardownCmd)
+ : m_teardownCmd(teardownCmd)
+ {
+ runShellScriptInChildAndWait(setupCmd);
+ }
+
+ ~ScopedShellScriptRunner()
+ {
+ SafeCleanup::run([this]() {
+ runShellScriptInChildAndWait(m_teardownCmd);
+ });
+ }
+
+ ScopedShellScriptRunner(const ScopedShellScriptRunner &) = delete;
+ ScopedShellScriptRunner &operator=(const ScopedShellScriptRunner &) = delete;
+
+private:
+ std::string m_teardownCmd;
+};
+
+
+void createChildProcess(const std::function<int(int)> &procedure, pid_t &childPid, int &childPipeFd)
+{
+ int pipeFd[2];
+ RUNNER_ASSERT_ERRNO_MSG(pipe2(pipeFd, O_DIRECT) == 0, "pipe() failed");
+
+ pid_t pid = fork();
+ RUNNER_ASSERT_ERRNO_MSG(pid != -1, "fork() failed");
+
+ if (pid != 0) {
+ // parent code
+ TEMP_FAILURE_RETRY(close(pipeFd[1]));
+
+ childPipeFd = pipeFd[0];
+ childPid = pid;
+ } else {
+ // child code
+ TestRunnerSingleton::Instance().Terminate();
+
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ close(pipeFd[0]);
+
+ int retStatus = EXIT_FAILURE;
+
+ int devNullFd = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY));
+ if (devNullFd == -1) {
+ goto end;
+ }
+
+ if (TEMP_FAILURE_RETRY(dup2(devNullFd, STDIN_FILENO)) == -1) {
+ goto end;
+ }
+
+ if (TEMP_FAILURE_RETRY(dup2(devNullFd, STDOUT_FILENO)) == -1) {
+ goto end;
+ }
+
+ if (TEMP_FAILURE_RETRY(dup2(devNullFd, STDERR_FILENO)) == -1) {
+ goto end;
+ }
+
+ retStatus = procedure(pipeFd[1]);
+end:
+ exit(retStatus);
+ }
+}
+
+
+int switchToNetworkNamespace(const std::string &netnsName)
+{
+ if (netnsName == NETHER_NETNS_NAME_NONE) {
+ return 0;
+ }
+
+ const std::string netnsPath { NETNS_RUN_DIR + "/" + netnsName };
+ if (netnsPath.length() >= PATH_MAX) {
+ return -1;
+ }
+
+ int netNsFd = open(netnsPath.c_str(), O_RDONLY);
+ if (netNsFd == -1) {
+ return -1;
+ }
+
+ // uses an existing network name-space (previously created by "ip netns" command)
+ if (setns(netNsFd, CLONE_NEWNET) == -1) {
+ close(netNsFd);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+class TemporaryNormalTestUser
+{
+public:
+ TemporaryNormalTestUser(const std::string &userName)
+ : m_user(userName, GUM_USERTYPE_NORMAL, false)
+ {
+ m_user.create();
+ }
+
+ uid_t getUid() const
+ {
+ return m_user.getUid();
+ }
+
+private:
+ TemporaryTestUser m_user;
+};
+
+
+class InternetLocalAppInstallHelper : public AppInstallHelper
+{
+public:
+ InternetLocalAppInstallHelper(const std::string &namePrefix, uid_t uid, NetherInternetAccess access)
+ : AppInstallHelper(namePrefix, uid)
+ {
+ setInstallType(app_install_type::SM_APP_INSTALL_LOCAL);
+ if (access == NetherInternetAccess::ACCESS_GRANTED) {
+ addPrivilege(INTERNET_ACCESS_PRIVILEGE);
+ }
+ }
+};
+
+
+class AppContext
+{
+public:
+ AppContext()
+ : m_appId{}
+ , m_appUID(-1)
+ , m_appGID(-1)
+ {
+ }
+
+ AppContext(const std::string &appId, uid_t appUID, gid_t appGID)
+ : m_appId(appId)
+ , m_appUID(appUID)
+ , m_appGID(appGID)
+ {
+ }
+
+ const std::string &appId() const
+ {
+ return m_appId;
+ }
+
+ void setAppId(const std::string &appId)
+ {
+ m_appId = appId;
+ }
+
+ uid_t getUID() const
+ {
+ return m_appUID;
+ }
+
+ void setUID(uid_t appUID)
+ {
+ m_appUID = appUID;
+ }
+
+ gid_t getGID() const
+ {
+ return m_appGID;
+ }
+
+ void setGID(gid_t appGID)
+ {
+ m_appGID = appGID;
+ }
+
+private:
+ std::string m_appId;
+ uid_t m_appUID;
+ gid_t m_appGID;
+};
+
+
+class ScopedAppContext : public AppContext
+{
+public:
+ ScopedAppContext(const std::string &appPrefix, NetherInternetAccess access)
+ : m_user(appPrefix + "_user")
+ , m_installHelper(appPrefix + "_app_normal", m_user.getUid(), access)
+ , m_scopedInstaller(m_installHelper)
+ {
+ setAppId(m_installHelper.getAppId());
+ setUID(m_installHelper.getUID());
+ setGID(m_installHelper.getGID());
+ }
+
+private:
+ TemporaryNormalTestUser m_user;
+ InternetLocalAppInstallHelper m_installHelper;
+ ScopedInstaller m_scopedInstaller;
+};
+
+
+void runProcedureInNetAppContext(
+ const std::string &appPrefix,
+ const std::function<void(void)> &procedure,
+ const NetherInternetAccess access = NetherInternetAccess::ACCESS_DENIED)
+{
+ ScopedAppContext scopedAppContext(appPrefix, access);
+
+ auto smackLabel = scopedAppContext.appId();
+ auto appUID = scopedAppContext.getUID();
+ auto appGID = scopedAppContext.getGID();
+
+ // run client procedure in app context
+ runInChildParentWait([=]() {
+ Api::setProcessLabel(smackLabel);
+ RUNNER_ASSERT_ERRNO_MSG(drop_root_privileges(appUID, appGID) == 0,
+ "drop_root_privileges() failed");
+ procedure();
+ });
+}
+
+
+class NetServer
+{
+public:
+ NetServer()
+ : m_monitorIsRunning(false)
+ , m_receivedBytes(0ull)
+ , m_serverPid(-1)
+ , m_serverPipeFd(-1)
+ {}
+
+ NetServer(const NetServer &) = delete;
+ NetServer &operator=(const NetServer &) = delete;
+
+ virtual ~NetServer()
+ {
+ SafeCleanup::run([this]() {
+ stop();
+ });
+ }
+
+ void start(const std::string &netnsName = NETHER_NETNS_NAME_NONE)
+ {
+ std::unique_lock<std::mutex> lock(m_monitorMutex);
+
+ RUNNER_ASSERT_MSG(!m_monitorIsRunning, "Monitor thread is already running");
+
+ createChildProcess([=] (int pipeFd) -> int {
+ if (netnsName != NETHER_NETNS_NAME_NONE) {
+ if (switchToNetworkNamespace(netnsName) == -1) {
+ exit(EXIT_FAILURE);
+ }
+ }
+ return serverProcedure(pipeFd);
+ }, m_serverPid, m_serverPipeFd);
+
+ waitForServer();
+
+ m_monitorThread = std::move(std::thread(&NetServer::monitorThreadProc, this));
+
+ m_monitorSyncPoint.wait(lock, [this]() {
+ return m_monitorIsRunning;
+ });
+ }
+
+ void stop()
+ {
+ stopMonitor();
+ closeServerPipe();
+ stopServerProcess();
+ }
+
+ uint64_t getReceivedBytes() const
+ {
+ RUNNER_ASSERT_MSG(!isAlive(), "Cannot read statistics. Server is still running: " << (*this));
+ return m_receivedBytes;
+ }
+
+ bool isAlive() const
+ {
+ int status;
+ return waitpid(m_serverPid, &status, WNOHANG) == 0;
+ }
+
+ virtual int serverProcedure(int) = 0;
+
+ virtual std::string getDescription() const = 0;
+
+protected:
+ void notifyManager(int pipeFd)
+ {
+ char c = 'X';
+ ssize_t ret = TEMP_FAILURE_RETRY(write(pipeFd, &c, sizeof(c)));
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+ }
+
+private:
+ void waitForServer() const
+ {
+ pollfd pfd { m_serverPipeFd, POLLIN, 0 };
+
+ int ret = TEMP_FAILURE_RETRY(poll(&pfd, 1, MONITOR_TIMEOUT));
+ RUNNER_ASSERT_ERRNO_MSG(ret >= 0, "poll() failed");
+
+ if (pfd.revents & POLLIN) {
+ char c;
+ ssize_t len = TEMP_FAILURE_RETRY(read(m_serverPipeFd, &c, sizeof(c)));
+ RUNNER_ASSERT_ERRNO_MSG(len >= 0, "Read pipe failed");
+ }
+ }
+
+ ssize_t collectStatistics()
+ {
+ char buffer[NET_BUFFER_SIZE];
+
+ ssize_t ret = TEMP_FAILURE_RETRY(read(m_serverPipeFd, buffer, sizeof(buffer)));
+ if (ret > 0) {
+ m_receivedBytes += static_cast<uint64_t>(ret);
+ }
+
+ return ret;
+ }
+
+ void closeServerPipe()
+ {
+ if (m_serverPipeFd == -1) {
+ return;
+ }
+
+ close(m_serverPipeFd);
+ m_serverPipeFd = -1;
+ }
+
+ void stopServerProcess()
+ {
+ if (m_serverPid == -1) {
+ return;
+ }
+
+ if (!isAlive()) {
+ m_serverPid = -1;
+ return;
+ }
+
+ RUNNER_ASSERT_ERRNO_MSG(kill(m_serverPid, SIGTERM) == 0, "kill() failed");
+
+ int status;
+ do {
+ pid_t ret = TEMP_FAILURE_RETRY(waitpid(m_serverPid, &status, WUNTRACED | WCONTINUED));
+ RUNNER_ASSERT_ERRNO_MSG(ret != -1, "waitpid() failed");
+ } while (!WIFEXITED(status) && !WIFSIGNALED(status));
+
+ m_serverPid = -1;
+ }
+
+ void stopMonitor()
+ {
+ {
+ std::lock_guard<std::mutex> lock(m_monitorMutex);
+ m_monitorIsRunning = false;
+ }
+
+ if (m_monitorThread.joinable()) {
+ m_monitorThread.join();
+ }
+ }
+
+ void monitorThreadProc()
+ {
+ std::unique_lock<std::mutex> lock(m_monitorMutex);
+ m_monitorIsRunning = true;
+ lock.unlock();
+
+ m_monitorSyncPoint.notify_one();
+
+ lock.lock();
+
+ pollfd pollFd{ m_serverPipeFd, POLLIN, 0 };
+
+ while (m_monitorIsRunning) {
+
+ lock.unlock();
+
+ int ret = TEMP_FAILURE_RETRY(poll(&pollFd, 1, MONITOR_TIMEOUT));
+ if (ret == -1) {
+ lock.lock();
+ break;
+ }
+
+ if (ret > 0 && (pollFd.revents & POLLIN)) {
+ if (collectStatistics() == -1) {
+ lock.lock();
+ break;
+ }
+ }
+
+ lock.lock();
+ }
+
+ m_monitorIsRunning = false;
+ lock.unlock();
+ }
+
+ // monitor variables
+ std::thread m_monitorThread;
+ bool m_monitorIsRunning;
+ std::mutex m_monitorMutex;
+ std::condition_variable m_monitorSyncPoint;
+
+ // server variables
+ uint64_t m_receivedBytes;
+ pid_t m_serverPid;
+ int m_serverPipeFd;
+
+ friend std::ostream &operator<<(std::ostream &os, const NetServer &);
+};
+
+
+std::ostream &operator<<(std::ostream &os, const NetServer &server)
+{
+ os << server.getDescription();
+ return os;
+}
+
+
+class UDPServer : public NetServer
+{
+public:
+ UDPServer(uint16_t port, int protocol = IPPROTO_UDP)
+ : NetServer{}
+ , m_port(port)
+ , m_protocol(protocol)
+ {}
+
+ virtual ~UDPServer() {}
+
+ virtual std::string getDescription() const override
+ {
+ return "UDPServer port: " + std::to_string(m_port);
+ }
+
+ virtual bool applyExtraSocketOptions(int)
+ {
+ return true;
+ }
+
+ virtual int serverProcedure(int pipeFd) override
+ {
+ FdUniquePtr pipePtr(&pipeFd);
+
+ int sockFd = socket(AF_INET, SOCK_DGRAM, m_protocol);
+ if (sockFd == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ SockUniquePtr sockPtr(&sockFd);
+
+ int optionValue = 1;
+ if (setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, static_cast<const void *>(&optionValue) , sizeof(int)) == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ struct sockaddr_in serverAddress;
+ memset(&serverAddress, 0, sizeof(serverAddress));
+ serverAddress.sin_family = AF_INET;
+ serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
+ serverAddress.sin_port = htons(m_port);
+
+ if (bind(sockFd, reinterpret_cast<struct sockaddr *>(&serverAddress), sizeof(serverAddress)) == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ if (!applyExtraSocketOptions(sockFd)) {
+ exit(EXIT_FAILURE);
+ }
+
+ struct sockaddr_in clntAddress;
+ socklen_t clntAddressLength = static_cast<socklen_t>(sizeof(clntAddress));
+ char receiveBuffer[NET_BUFFER_SIZE];
+
+ notifyManager(pipeFd);
+
+ while (true) {
+ ssize_t len = TEMP_FAILURE_RETRY(recvfrom(sockFd, receiveBuffer, sizeof(receiveBuffer), 0,
+ reinterpret_cast<struct sockaddr *>(&clntAddress), &clntAddressLength));
+
+ if (len == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ if (len == 0) {
+ continue;
+ }
+
+ do {
+ ssize_t pos = 0;
+ ssize_t ret = TEMP_FAILURE_RETRY(write(pipeFd, &receiveBuffer[pos], len));
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ len -= ret;
+ pos += ret;
+ } while (len != 0);
+ }
+
+ return EXIT_SUCCESS;
+ }
+
+private:
+ uint16_t m_port;
+ int m_protocol;
+};
+
+
+class UDPServerApp : public UDPServer
+{
+public:
+ UDPServerApp(uint16_t port, int protocol, const AppContext &appContext)
+ : UDPServer(port, protocol)
+ , m_appContext(appContext)
+ {
+ }
+
+ virtual int serverProcedure(int pipeFd) override
+ {
+ Api::setProcessLabel(m_appContext.appId());
+ RUNNER_ASSERT_ERRNO_MSG(drop_root_privileges(m_appContext.getUID(), m_appContext.getGID()) == 0,
+ "drop_root_privileges() failed");
+
+ return UDPServer::serverProcedure(pipeFd);
+ }
+
+private:
+ AppContext m_appContext;
+};
+
+class UDPMulticastServer : public UDPServer
+{
+public:
+ UDPMulticastServer(uint16_t port, int protocol,
+ const std::string &mcastGroup, const std::string &networkInterface)
+ : UDPServer(port, protocol)
+ , m_mcastGroup(mcastGroup)
+ , m_networkInterface(networkInterface)
+ {
+ }
+
+ bool applyExtraSocketOptions(int sockFd) override
+ {
+ struct ip_mreq multicastRequest;
+ memset(&multicastRequest, 0, sizeof(multicastRequest));
+
+ in_addr_t mcastGroupAddr = inet_addr(m_mcastGroup.c_str());
+ if (!IN_MULTICAST(ntohl(mcastGroupAddr))) {
+ return false;
+ }
+
+ multicastRequest.imr_multiaddr.s_addr = mcastGroupAddr;
+
+ if (m_networkInterface == ANY_INTERFACE) {
+ multicastRequest.imr_interface.s_addr = INADDR_ANY;
+ } else {
+ struct ifreq interfaceRequest;
+ memset(&interfaceRequest, 0, sizeof(interfaceRequest));
+
+ interfaceRequest.ifr_addr.sa_family = AF_INET;
+ strncpy(interfaceRequest.ifr_name, m_networkInterface.c_str(), IFNAMSIZ);
+ interfaceRequest.ifr_name[IFNAMSIZ - 1] = '\0';
+
+ if (ioctl(sockFd, SIOCGIFADDR, &interfaceRequest) == -1) {
+ return false;
+ }
+
+ struct sockaddr_in *addr = reinterpret_cast<struct sockaddr_in *>(&interfaceRequest.ifr_addr);
+ multicastRequest.imr_interface.s_addr = addr->sin_addr.s_addr;
+ }
+
+ return setsockopt(sockFd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ static_cast<const void *>(&multicastRequest), sizeof(multicastRequest)) == 0;
+ }
+
+private:
+ std::string m_mcastGroup;
+ std::string m_networkInterface;
+};
+
+
+class UDPMulticastServerApp : public UDPMulticastServer
+{
+public:
+ UDPMulticastServerApp(uint16_t port, int protocol, const std::string &mcastGroup, const std::string &networkInterface,
+ const AppContext &appContext)
+ : UDPMulticastServer(port, protocol, mcastGroup, networkInterface)
+ , m_appContext(appContext)
+ {
+ }
+
+ virtual int serverProcedure(int pipeFd) override
+ {
+ Api::setProcessLabel(m_appContext.appId());
+ RUNNER_ASSERT_ERRNO_MSG(drop_root_privileges(m_appContext.getUID(), m_appContext.getGID()) == 0,
+ "drop_root_privileges() failed");
+
+ return UDPServer::serverProcedure(pipeFd);
+ }
+
+private:
+ AppContext m_appContext;
+};
+
+
+class TCPServer : public NetServer
+{
+public:
+ explicit TCPServer(uint16_t port)
+ : NetServer{}
+ , m_port(port)
+ {}
+
+ virtual ~TCPServer() {}
+
+ virtual std::string getDescription() const override
+ {
+ return "TCPServer port: " + std::to_string(m_port);
+ }
+
+ virtual int serverProcedure(int pipeFd) override
+ {
+ FdUniquePtr pipePtr(&pipeFd);
+
+ int sockFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sockFd == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ SockUniquePtr sockPtr(&sockFd);
+
+ int optionValue = 1;
+ if (setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, static_cast<const void *>(&optionValue) , sizeof(int)) == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ struct sockaddr_in serverAddress;
+ memset(&serverAddress, 0, sizeof(serverAddress));
+ serverAddress.sin_family = AF_INET;
+ serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
+ serverAddress.sin_port = htons(m_port);
+
+ if (bind(sockFd, reinterpret_cast<struct sockaddr *>(&serverAddress), sizeof(serverAddress)) == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ if (listen(sockFd, 1) == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ struct sockaddr_in clntAddress;
+ socklen_t clntAddressLength = static_cast<socklen_t>(sizeof(clntAddress));
+ char receiveBuffer[NET_BUFFER_SIZE];
+
+ notifyManager(pipeFd);
+
+ while (true) {
+ int acceptedSocketFd = TEMP_FAILURE_RETRY(accept(sockFd, reinterpret_cast<struct sockaddr *>(&clntAddress),
+ &clntAddressLength));
+ if (acceptedSocketFd == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ SockUniquePtr acceptSockPtr(&acceptedSocketFd);
+
+ while (true) {
+ ssize_t len = TEMP_FAILURE_RETRY(recv(acceptedSocketFd, receiveBuffer, sizeof(receiveBuffer), 0));
+ if (len == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ if (len == 0) {
+ break;
+ }
+
+ do {
+ ssize_t pos = 0;
+ ssize_t ret = TEMP_FAILURE_RETRY(write(pipeFd, &receiveBuffer[pos], len));
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ len -= ret;
+ pos += ret;
+ } while (len != 0);
+ }
+ }
+
+ return EXIT_SUCCESS;
+ }
+
+private:
+ uint16_t m_port;
+};
+
+
+class TCPServerApp : public TCPServer
+{
+public:
+ TCPServerApp(int port, const AppContext &appContext)
+ : TCPServer(port)
+ , m_appContext(appContext)
+ {
+ }
+
+ virtual int serverProcedure(int pipeFd) override
+ {
+ Api::setProcessLabel(m_appContext.appId());
+ RUNNER_ASSERT_ERRNO_MSG(drop_root_privileges(m_appContext.getUID(), m_appContext.getGID()) == 0,
+ "drop_root_privileges() failed");
+
+ return TCPServer::serverProcedure(pipeFd);
+ }
+
+private:
+ AppContext m_appContext;
+};
+
+
+class NetClient
+{
+public:
+ NetClient(const std::string &appPrefix, NetherInternetAccess access, int msgCount)
+ : m_appPrefix(appPrefix)
+ , m_access(access)
+ , m_msgCount(msgCount)
+ {
+ }
+
+ NetClient(const NetClient &) = delete;
+ NetClient &operator=(const NetClient &) = delete;
+
+ void start()
+ {
+ runProcedureInNetAppContext(m_appPrefix, [this]() { clientProcedure(); } , m_access);
+ }
+
+ virtual void clientProcedure() = 0;
+
+protected:
+ std::string m_appPrefix;
+ NetherInternetAccess m_access;
+ int m_msgCount;
+};
+
+class UDPClientApp : public NetClient
+{
+public:
+ UDPClientApp(const std::string &appPrefix, NetherInternetAccess access, int msgCount,
+ int protocol, const std::string &hostName, uint16_t port)
+ : NetClient(appPrefix, access, msgCount)
+ , m_protocol(protocol)
+ , m_hostName(hostName)
+ , m_port(port)
+ {
+ }
+
+ virtual bool applyExtraSocketOptions(int)
+ {
+ return true;
+ }
+
+private:
+ virtual void clientProcedure() override
+ {
+ int sockFd = socket(AF_INET, SOCK_DGRAM, m_protocol);
+ RUNNER_ASSERT_ERRNO_MSG(sockFd >= 0, "socket() failed");
+ SockUniquePtr sockPtr(&sockFd);
+
+ struct hostent *server = gethostbyname(m_hostName.c_str());
+ RUNNER_ASSERT_MSG(server != nullptr, "Couldn't find host " << m_hostName
+ << " h_errno = " << hstrerror(h_errno));
+
+ RUNNER_ASSERT_MSG (applyExtraSocketOptions(sockFd), "Couldn't prepare socket");
+
+ struct sockaddr_in serverAddress;
+ memset(&serverAddress, 0, sizeof(serverAddress));
+ serverAddress.sin_family = AF_INET;
+ memcpy(&serverAddress.sin_addr.s_addr, server->h_addr, server->h_length);
+ serverAddress.sin_port = htons(m_port);
+
+ char sendBuffer[NET_BUFFER_SIZE];
+
+ memset(sendBuffer, 'X', sizeof(sendBuffer));
+ size_t serverAddressLength = sizeof(serverAddress);
+
+ int msgCount = m_msgCount;
+ while (msgCount-- > 0) {
+ ssize_t len = sizeof(sendBuffer);
+ ssize_t pos = 0;
+ do {
+ ssize_t ret = TEMP_FAILURE_RETRY(sendto(sockFd, &sendBuffer[pos], len, 0,
+ reinterpret_cast<struct sockaddr *>(&serverAddress), serverAddressLength));
+ RUNNER_ASSERT_ERRNO_MSG(ret >= 0, "sendto() failed");
+ len -= ret;
+ pos += ret;
+ } while (len != 0);
+ }
+ }
+
+ int m_protocol;
+ std::string m_hostName;
+ uint16_t m_port;
+};
+
+class UDPMulticastClientApp : public UDPClientApp
+{
+public:
+ UDPMulticastClientApp(const std::string &appPrefix, NetherInternetAccess access, int msgCount,
+ int protocol, const std::string &hostName, uint16_t port, char ttl, bool enableLoop)
+ : UDPClientApp(appPrefix, access, msgCount, protocol, hostName, port)
+ , m_ttl(ttl)
+ , m_enableLoop(enableLoop)
+ {
+ }
+
+ virtual bool applyExtraSocketOptions(int sockFd) override
+ {
+ if (setsockopt(sockFd, IPPROTO_IP, IP_MULTICAST_TTL, static_cast<void *>(&m_ttl), sizeof(m_ttl)) == -1) {
+ return false;
+ }
+
+ char multicastEnableLoop = m_enableLoop ? 1 : 0;
+ return setsockopt(sockFd, IPPROTO_IP, IP_MULTICAST_LOOP, static_cast<void *>(&multicastEnableLoop),
+ sizeof(multicastEnableLoop)) == 0;
+ }
+
+private:
+ char m_ttl;
+ bool m_enableLoop;
+};
+
+class TCPClientApp : public NetClient
+{
+public:
+ TCPClientApp(const std::string &appPrefix, NetherInternetAccess access, int msgCount,
+ const std::string &hostName, int port, bool expectConnectionError)
+ : NetClient(appPrefix, access, msgCount)
+ , m_hostName(hostName)
+ , m_port(port)
+ , m_expectConnectionError(expectConnectionError)
+ {
+ }
+
+private:
+ virtual void clientProcedure() override
+ {
+ int sockFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ RUNNER_ASSERT_ERRNO_MSG(sockFd >= 0, "socket() failed");
+ SockUniquePtr sockPtr(&sockFd);
+
+ struct hostent *server = gethostbyname(m_hostName.c_str());
+ RUNNER_ASSERT_MSG(server != nullptr, "Couldn't find host " << m_hostName
+ << " h_errno = " << hstrerror(h_errno));
+
+ struct sockaddr_in serverAddress;
+ memset(&serverAddress, 0, sizeof(serverAddress));
+ serverAddress.sin_family = AF_INET;
+ memcpy(&serverAddress.sin_addr.s_addr, server->h_addr, server->h_length);
+ serverAddress.sin_port = htons(m_port);
+ size_t serverAddressLength = sizeof(serverAddress);
+
+ int ret = TEMP_FAILURE_RETRY(connect(sockFd,
+ reinterpret_cast<struct sockaddr *>(&serverAddress),serverAddressLength));
+ if (ret == -1 && m_expectConnectionError) {
+ return;
+ }
+
+ RUNNER_ASSERT_ERRNO_MSG(ret == 0, "Couldn't connect to " << m_hostName);
+
+ char sendBuffer[NET_BUFFER_SIZE];
+ memset(sendBuffer, 'X', sizeof(sendBuffer));
+
+ int msgCount = m_msgCount;
+ while (msgCount-- > 0) {
+ ssize_t len = sizeof(sendBuffer);
+ ssize_t pos = 0;
+ do {
+ ssize_t ret = TEMP_FAILURE_RETRY(send(sockFd, &sendBuffer[pos], len, 0));
+ RUNNER_ASSERT_ERRNO_MSG(ret >= 0, "send() failed");
+ len -= ret;
+ pos += ret;
+ } while (len != 0);
+ }
+ }
+
+ std::string m_hostName;
+ uint16_t m_port;
+ bool m_expectConnectionError;
+};
+
+
+RUNNER_TEST_GROUP_INIT(NETHER_REMOTE_CONNECTION)
+
+
+RUNNER_CHILD_TEST(nether_check_udp_connection_internet_access_granted)
+{
+ ScopedShellScriptRunner networkNSRunner(NETHER_NETNS_SETUP_COMMAND,
+ NETHER_NETNS_TEARDOWN_COMMAND);
+
+ UDPServer udpServer(UDP_TEST_PORT);
+ udpServer.start(NETHER_NETNS_NAME_TEST);
+
+ UDPClientApp udpClientApp("nether_test_uciag", NetherInternetAccess::ACCESS_GRANTED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDP, REMOTE_INTERFACE_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpServer.isAlive(), "UDP server was not running");
+ udpServer.stop();
+ RUNNER_ASSERT_MSG(udpServer.getReceivedBytes() > 0, "UDP server didn't receive any data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_udp_connection_internet_access_denied)
+{
+ ScopedShellScriptRunner networkNSRunner(NETHER_NETNS_SETUP_COMMAND,
+ NETHER_NETNS_TEARDOWN_COMMAND);
+
+ UDPServer udpServer(UDP_TEST_PORT);
+ udpServer.start(NETHER_NETNS_NAME_TEST);
+
+ UDPClientApp udpClientApp("nether_test_uciad", NetherInternetAccess::ACCESS_DENIED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDP, REMOTE_INTERFACE_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpServer.isAlive(), "UDP server was not running");
+ udpServer.stop();
+ RUNNER_ASSERT_MSG(udpServer.getReceivedBytes() == 0, "UDP server received some data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_udp_lite_connection_internet_access_granted)
+{
+ ScopedShellScriptRunner networkNSRunner(NETHER_NETNS_SETUP_COMMAND,
+ NETHER_NETNS_TEARDOWN_COMMAND);
+
+ UDPServer udpLiteServer(UDP_TEST_PORT, IPPROTO_UDPLITE);
+ udpLiteServer.start(NETHER_NETNS_NAME_TEST);
+
+ UDPClientApp udpClientApp("nether_test_ulciag", NetherInternetAccess::ACCESS_GRANTED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDPLITE, REMOTE_INTERFACE_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpLiteServer.isAlive(), "UDPLite server was not running");
+ udpLiteServer.stop();
+ RUNNER_ASSERT_MSG(udpLiteServer.getReceivedBytes() > 0, "UDPLite server didn't receive any data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_udp_lite_connection_internet_access_denied)
+{
+ ScopedShellScriptRunner networkNSRunner(NETHER_NETNS_SETUP_COMMAND,
+ NETHER_NETNS_TEARDOWN_COMMAND);
+
+ UDPServer udpLiteServer(UDP_TEST_PORT, IPPROTO_UDPLITE);
+ udpLiteServer.start(NETHER_NETNS_NAME_TEST);
+
+ UDPClientApp udpClientApp("nether_test_ulciad", NetherInternetAccess::ACCESS_DENIED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDPLITE, REMOTE_INTERFACE_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpLiteServer.isAlive(), "UDP server was not running");
+ udpLiteServer.stop();
+ RUNNER_ASSERT_MSG(udpLiteServer.getReceivedBytes() == 0, "UDP server received some data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_tcp_connection_internet_access_granted)
+{
+ ScopedShellScriptRunner networkNSRunner(NETHER_NETNS_SETUP_COMMAND,
+ NETHER_NETNS_TEARDOWN_COMMAND);
+
+ TCPServer tcpServer(TCP_TEST_PORT);
+ tcpServer.start(NETHER_NETNS_NAME_TEST);
+
+ TCPClientApp tcpClientApp("nether_test_tciag", NetherInternetAccess::ACCESS_GRANTED, TCP_MESSAGES_COUNT,
+ REMOTE_INTERFACE_ADDRESS, TCP_TEST_PORT, false);
+ tcpClientApp.start();
+
+ RUNNER_ASSERT_MSG(tcpServer.isAlive(), "TCP server was not running");
+ tcpServer.stop();
+ RUNNER_ASSERT_MSG(tcpServer.getReceivedBytes() > 0, "TCP server didn't receive any data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_tcp_connection_internet_access_denied)
+{
+ ScopedShellScriptRunner networkNSRunner(NETHER_NETNS_SETUP_COMMAND,
+ NETHER_NETNS_TEARDOWN_COMMAND);
+
+ TCPServer tcpServer(TCP_TEST_PORT);
+ tcpServer.start(NETHER_NETNS_NAME_TEST);
+
+ TCPClientApp tcpClientApp("nether_test_tciad", NetherInternetAccess::ACCESS_DENIED, TCP_MESSAGES_COUNT,
+ REMOTE_INTERFACE_ADDRESS, TCP_TEST_PORT, true);
+ tcpClientApp.start();
+
+ RUNNER_ASSERT_MSG(tcpServer.isAlive(), "TCP server was not running");
+ tcpServer.stop();
+ RUNNER_ASSERT_MSG(tcpServer.getReceivedBytes() == 0, "TCP server received some data");
+}
+
+// Communication between application and local service
+RUNNER_TEST_GROUP_INIT(NETHER_LOCAL_SERVICE_CONNECTION)
+
+
+RUNNER_CHILD_TEST(nether_check_udp_local_connection_internet_access_granted)
+{
+ UDPServer udpLiteServer(UDP_TEST_PORT, IPPROTO_UDP);
+ udpLiteServer.start();
+
+ UDPClientApp udpClientApp("nether_test_ulciag", NetherInternetAccess::ACCESS_GRANTED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDP, LOCAL_HOST_TEST_SERVER_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpLiteServer.isAlive(), "UDP server was not running");
+ udpLiteServer.stop();
+ RUNNER_ASSERT_MSG(udpLiteServer.getReceivedBytes() > 0, "UDP server didn't receive any data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_udp_local_connection_internet_access_denied)
+{
+ UDPServer udpLiteServer(UDP_TEST_PORT, IPPROTO_UDP);
+ udpLiteServer.start();
+
+ UDPClientApp udpClientApp("nether_test_ulciad", NetherInternetAccess::ACCESS_DENIED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDP, LOCAL_HOST_TEST_SERVER_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpLiteServer.isAlive(), "UDP server was not running");
+ udpLiteServer.stop();
+ RUNNER_ASSERT_MSG(udpLiteServer.getReceivedBytes() > 0, "UDP server didn't receive any data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_udp_lite_local_connection_internet_access_granted)
+{
+ UDPServer udpLiteServer(UDP_TEST_PORT, IPPROTO_UDPLITE);
+ udpLiteServer.start();
+
+ UDPClientApp udpClientApp("nether_test_ullciag", NetherInternetAccess::ACCESS_GRANTED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDPLITE, LOCAL_HOST_TEST_SERVER_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpLiteServer.isAlive(), "UDP server was not running");
+ udpLiteServer.stop();
+ RUNNER_ASSERT_MSG(udpLiteServer.getReceivedBytes() > 0, "UDP server didn't receive any data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_udp_lite_local_connection_internet_access_denied)
+{
+ UDPServer udpLiteServer(UDP_TEST_PORT, IPPROTO_UDPLITE);
+ udpLiteServer.start();
+
+ UDPClientApp udpClientApp("nether_test_ullciad", NetherInternetAccess::ACCESS_DENIED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDPLITE, LOCAL_HOST_TEST_SERVER_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpLiteServer.isAlive(), "UDP server was not running");
+ udpLiteServer.stop();
+ RUNNER_ASSERT_MSG(udpLiteServer.getReceivedBytes() > 0, "UDP server didn't receive any data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_tcp_local_connection_internet_access_granted)
+{
+ TCPServer tcpServer(TCP_TEST_PORT);
+ tcpServer.start();
+
+ TCPClientApp tcpClientApp("nether_test_tlciad", NetherInternetAccess::ACCESS_GRANTED, TCP_MESSAGES_COUNT,
+ LOCAL_HOST_TEST_SERVER_ADDRESS, TCP_TEST_PORT, false);
+ tcpClientApp.start();
+
+ RUNNER_ASSERT_MSG(tcpServer.isAlive(), "TCP server was not running");
+ tcpServer.stop();
+ RUNNER_ASSERT_MSG(tcpServer.getReceivedBytes() > 0, "TCP server didn't receive any data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_tcp_local_connection_internet_access_denied)
+{
+ TCPServer tcpServer(TCP_TEST_PORT);
+ tcpServer.start();
+
+ TCPClientApp tcpClientApp("nether_test_tlciad", NetherInternetAccess::ACCESS_DENIED, TCP_MESSAGES_COUNT,
+ LOCAL_HOST_TEST_SERVER_ADDRESS, TCP_TEST_PORT, false);
+ tcpClientApp.start();
+
+ RUNNER_ASSERT_MSG(tcpServer.isAlive(), "TCP server was not running");
+ tcpServer.stop();
+ RUNNER_ASSERT_MSG(tcpServer.getReceivedBytes() > 0, "TCP server didn't receive any data");
+}
+
+
+// Applications mustn't communicate with each other even if access to the Internet is granted.
+RUNNER_TEST_GROUP_INIT(NETHER_LOCAL_INTER_APP_CONNECTION)
+
+
+RUNNER_CHILD_TEST(nether_check_udp_local_inter_app_connection_internet_access_granted)
+{
+ ScopedAppContext appContext("nether_test_uliaciag_a", NetherInternetAccess::ACCESS_GRANTED);
+ UDPServerApp udpServerApp(UDP_TEST_PORT, IPPROTO_UDP, appContext);
+ udpServerApp.start();
+
+ UDPClientApp udpClientApp("nether_test_uliaciag_b", NetherInternetAccess::ACCESS_GRANTED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDP, LOCAL_HOST_TEST_SERVER_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpServerApp.isAlive(), "UDP server was not running");
+ udpServerApp.stop();
+ RUNNER_ASSERT_MSG(udpServerApp.getReceivedBytes() == 0, "UDP server received some data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_udp_local_inter_app_connection_internet_access_denied)
+{
+ ScopedAppContext appContext("nether_test_uliaciad_a", NetherInternetAccess::ACCESS_DENIED);
+ UDPServerApp udpServerApp(UDP_TEST_PORT, IPPROTO_UDP, appContext);
+ udpServerApp.start();
+
+ UDPClientApp udpClientApp("nether_test_uliaciad_b", NetherInternetAccess::ACCESS_DENIED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDP, LOCAL_HOST_TEST_SERVER_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpServerApp.isAlive(), "UDP server was not running");
+ udpServerApp.stop();
+ RUNNER_ASSERT_MSG(udpServerApp.getReceivedBytes() == 0, "UDP server received some data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_udp_lite_local_inter_app_connection_internet_access_granted)
+{
+ ScopedAppContext appContext("nether_test_ulliaciag_a", NetherInternetAccess::ACCESS_GRANTED);
+ UDPServerApp udpLiteServerApp(UDP_TEST_PORT, IPPROTO_UDPLITE, appContext);
+ udpLiteServerApp.start();
+
+ UDPClientApp udpClientApp("nether_ulliaciag_b", NetherInternetAccess::ACCESS_GRANTED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDPLITE, LOCAL_HOST_TEST_SERVER_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpLiteServerApp.isAlive(), "UDP server was not running");
+ udpLiteServerApp.stop();
+ RUNNER_ASSERT_MSG(udpLiteServerApp.getReceivedBytes() == 0, "UDP server received some data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_udp_lite_local_inter_app_connection_internet_access_denied)
+{
+ ScopedAppContext appContext("nether_test_ulliaciad_a", NetherInternetAccess::ACCESS_DENIED);
+ UDPServerApp udpLiteServerApp(UDP_TEST_PORT, IPPROTO_UDPLITE, appContext);
+ udpLiteServerApp.start();
+
+ UDPClientApp udpClientApp("nether_ulliaciad_b", NetherInternetAccess::ACCESS_DENIED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDPLITE, LOCAL_HOST_TEST_SERVER_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpLiteServerApp.isAlive(), "UDP server was not running");
+ udpLiteServerApp.stop();
+ RUNNER_ASSERT_MSG(udpLiteServerApp.getReceivedBytes() == 0, "UDP server received some data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_tcp_local_inter_app_connection_internet_access_granted)
+{
+ ScopedAppContext appContext("nether_test_tliaciag_a", NetherInternetAccess::ACCESS_GRANTED);
+ TCPServerApp tcpServerApp(TCP_TEST_PORT, appContext);
+ tcpServerApp.start();
+
+ TCPClientApp tcpClientApp("nether_test_tliaciag_b", NetherInternetAccess::ACCESS_GRANTED, TCP_MESSAGES_COUNT,
+ LOCAL_HOST_TEST_SERVER_ADDRESS, TCP_TEST_PORT, true);
+ tcpClientApp.start();
+
+ RUNNER_ASSERT_MSG(tcpServerApp.isAlive(), "TCP server was not running");
+ tcpServerApp.stop();
+ RUNNER_ASSERT_MSG(tcpServerApp.getReceivedBytes() == 0, "TCP server received some data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_tcp_local_inter_app_connection_internet_access_denied)
+{
+ ScopedAppContext appContext("nether_test_tliaciad_a", NetherInternetAccess::ACCESS_DENIED);
+ TCPServerApp tcpServerApp(TCP_TEST_PORT, appContext);
+ tcpServerApp.start();
+
+ TCPClientApp tcpClientApp("nether_test_tliaciad_b", NetherInternetAccess::ACCESS_DENIED, TCP_MESSAGES_COUNT,
+ LOCAL_HOST_TEST_SERVER_ADDRESS, TCP_TEST_PORT, true);
+ tcpClientApp.start();
+
+ RUNNER_ASSERT_MSG(tcpServerApp.isAlive(), "TCP server was not running");
+ tcpServerApp.stop();
+ RUNNER_ASSERT_MSG(tcpServerApp.getReceivedBytes() == 0, "TCP server received some data");
+}
+
+
+RUNNER_TEST_GROUP_INIT(NETHER_MULTICAST)
+
+
+RUNNER_CHILD_TEST(nether_check_multicast_inter_app_connection_access_granted)
+{
+ // one app acts as a server (receiver), the second as a sender
+ ScopedAppContext appContext("nether_test_miacag_a", NetherInternetAccess::ACCESS_GRANTED);
+ UDPMulticastServerApp udpMulticastServerApp(UDP_TEST_PORT, IPPROTO_UDP, LOCAL_TEST_MCAST_GROUP,
+ ANY_INTERFACE, appContext);
+ udpMulticastServerApp.start();
+
+ UDPMulticastClientApp udpMulticastClientApp("nether_test_miacag_b", NetherInternetAccess::ACCESS_GRANTED,
+ UDP_MESSAGES_COUNT, IPPROTO_UDP, LOCAL_TEST_MCAST_GROUP, UDP_TEST_PORT, TTL_MCAST_RESTRIC_SUBNET, true);
+ udpMulticastClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpMulticastServerApp.isAlive(), "UDP multicast server was not running");
+ udpMulticastServerApp.stop();
+ RUNNER_ASSERT_MSG(udpMulticastServerApp.getReceivedBytes() > 0, "UDP multicast server didn't receive any data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_multicast_inter_app_connection_access_denied)
+{
+ ScopedAppContext appContext("nether_test_miacad_a", NetherInternetAccess::ACCESS_DENIED);
+ UDPMulticastServerApp udpMulticastServerApp(UDP_TEST_PORT, IPPROTO_UDP, LOCAL_TEST_MCAST_GROUP,
+ ANY_INTERFACE, appContext);
+ udpMulticastServerApp.start();
+
+ UDPMulticastClientApp udpMulticastClientApp("nether_test_miacad_b", NetherInternetAccess::ACCESS_DENIED,
+ UDP_MESSAGES_COUNT, IPPROTO_UDP, LOCAL_TEST_MCAST_GROUP, UDP_TEST_PORT, TTL_MCAST_RESTRIC_SUBNET, true);
+ udpMulticastClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpMulticastServerApp.isAlive(), "UDP multicast server was not running");
+ udpMulticastServerApp.stop();
+ RUNNER_ASSERT_MSG(udpMulticastServerApp.getReceivedBytes() == 0, "UDP multicast server received some data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_multicast_remote_connection_app_sender_access_granted)
+{
+ ScopedShellScriptRunner networkNSRunner(NETHER_NETNS_SETUP_COMMAND,
+ NETHER_NETNS_TEARDOWN_COMMAND);
+
+ UDPMulticastServer udpMulticastServer(UDP_TEST_PORT, IPPROTO_UDP, LOCAL_TEST_MCAST_GROUP, REMOTE_INTERFACE_NAME);
+ udpMulticastServer.start(NETHER_NETNS_NAME_TEST);
+
+ UDPMulticastClientApp udpMulticastClientApp("nether_test_mrcasag", NetherInternetAccess::ACCESS_GRANTED,
+ UDP_MESSAGES_COUNT, IPPROTO_UDP, LOCAL_TEST_MCAST_GROUP, UDP_TEST_PORT, TTL_MCAST_RESTRIC_SUBNET, false);
+ udpMulticastClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpMulticastServer.isAlive(), "UDP multicast server was not running");
+ udpMulticastServer.stop();
+ RUNNER_ASSERT_MSG(udpMulticastServer.getReceivedBytes() > 0, "UDP multicast server didn't receive any data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_multicast_remote_connection_app_sender_access_denied)
+{
+ ScopedShellScriptRunner networkNSRunner(NETHER_NETNS_SETUP_COMMAND,
+ NETHER_NETNS_TEARDOWN_COMMAND);
+
+ UDPMulticastServer udpMulticastServer(UDP_TEST_PORT, IPPROTO_UDP, LOCAL_TEST_MCAST_GROUP, REMOTE_INTERFACE_NAME);
+ udpMulticastServer.start(NETHER_NETNS_NAME_TEST);
+
+ UDPMulticastClientApp udpMulticastClientApp("nether_test_mrcasad", NetherInternetAccess::ACCESS_DENIED,
+ UDP_MESSAGES_COUNT, IPPROTO_UDP, LOCAL_TEST_MCAST_GROUP, UDP_TEST_PORT, TTL_MCAST_RESTRIC_SUBNET, false);
+ udpMulticastClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpMulticastServer.isAlive(), "UDP multicast server was not running");
+ udpMulticastServer.stop();
+ RUNNER_ASSERT_MSG(udpMulticastServer.getReceivedBytes() == 0, "UDP multicast server received some data");
+}
+
+
+RUNNER_TEST_GROUP_INIT(NETHER_LOCAL_DNS_CONNECTION)
+
+
+RUNNER_CHILD_TEST(nether_check_gethostbyname_internet_access_granted)
+{
+ ScopedShellScriptRunner networkDNSRunner(NETHER_DNS_SETUP_COMMAND,
+ NETHER_DNS_TEARDOWN_COMMAND);
+
+ const auto getHostAddress = [](void) {
+ struct hostent *server = gethostbyname(DNS_TEST_ADDRESS.c_str());
+ RUNNER_ASSERT_MSG(server != nullptr, "Couldn't find host "
+ << DNS_TEST_ADDRESS << " h_errno = " << h_errno);
+ };
+ runProcedureInNetAppContext("nether_test_giag", getHostAddress, NetherInternetAccess::ACCESS_GRANTED);
+}
+
+
+RUNNER_CHILD_TEST(nether_check_gethostbyname_internet_access_denied)
+{
+ ScopedShellScriptRunner networkDNSRunner(NETHER_DNS_SETUP_COMMAND,
+ NETHER_DNS_TEARDOWN_COMMAND);
+
+ const auto getHostAddress = [](void) {
+ struct hostent *server = gethostbyname(DNS_TEST_ADDRESS.c_str());
+ RUNNER_ASSERT_MSG(server == nullptr, "Host was found " << DNS_TEST_ADDRESS);
+ };
+
+ runProcedureInNetAppContext("nether_test_giad", getHostAddress, NetherInternetAccess::ACCESS_DENIED);
+}
+
+
+void checkRawSocket(void)
+{
+ const std::vector<int> protocols {
+ IPPROTO_ICMP,
+ IPPROTO_IGMP
+ };
+
+ for (const auto protocol : protocols) {
+ int sockFd = socket(AF_INET, SOCK_RAW, protocol);
+ RUNNER_ASSERT_ERRNO_MSG(sockFd == -1, "RAW socket was successfully created for protocol " << protocol);
+ }
+};
+
+
+RUNNER_TEST_GROUP_INIT(NETHER_RAW_SOCKET)
+
+
+RUNNER_CHILD_TEST(nether_check_raw_socket_access_granted)
+{
+ runProcedureInNetAppContext("nether_test_rsag", checkRawSocket, NetherInternetAccess::ACCESS_GRANTED);
+}
+
+
+RUNNER_CHILD_TEST(nether_check_raw_socket_access_denied)
+{
+ runProcedureInNetAppContext("nether_test_rsad", checkRawSocket, NetherInternetAccess::ACCESS_DENIED);
+}
+
+
+int main(int argc, char *argv[])
+{
+ return DPL::Test::TestRunnerSingleton::Instance().ExecTestRunner(argc, argv);
+}