--- /dev/null
+#ifndef NETHER_CYNARA_BACKEND_H
+#define NETHER_CYNARA_BACKEND_H
+
+// #ifdef HAVE_CYNARA
+
+#include <cynara-client-async.h>
+#include "nether_PolicyBackend.h"
+#include <vector>
+
+#define NETHER_CYNARA_INTERNET_PRIVILEGE "http://tizen.org/privilege/internet"
+
+static const std::string cynaraErrorCodeToString(int cynaraErrorCode)
+{
+ char errorString[512];
+ int ret;
+
+ if ((ret = cynara_strerror(cynaraErrorCode, errorString, 512)) == CYNARA_API_SUCCESS)
+ return (std::string(errorString, strlen(errorString)));
+ else
+ return ("Failed to get error string representation, code="+ret);
+}
+
+class NetherManager;
+
+class NetherCynaraBackend : public NetherPolicyBackend
+{
+ public:
+ NetherCynaraBackend(const NetherConfig &netherConfig);
+ ~NetherCynaraBackend();
+ const bool initialize();
+ const bool isValid();
+ const bool enqueueVerdict (const NetherPacket &packet);
+ const bool processEvents();
+ const int getDescriptor();
+ const NetherDescriptorStatus getDescriptorStatus();
+ void setCynaraDescriptor(const int _currentCynaraDescriptor, const NetherDescriptorStatus _currentCynaraDescriptorStatus);
+ void setCynaraVerdict(cynara_check_id checkId, int cynaraResult);
+ static void statusCallback(int oldFd, int newFd, cynara_async_status status, void *data);
+ static void checkCallback(cynara_check_id check_id, cynara_async_call_cause cause, int response, void *data);
+
+ private:
+ cynara_async *cynaraContext;
+ NetherDescriptorStatus currentCynaraDescriptorStatus;
+ int currentCynaraDescriptor;
+ std::vector<u_int32_t> responseQueue;
+ int cynaraLastResult;
+};
+
+// #endif
+#endif
--- /dev/null
+#ifndef NETHER_DUMMY_BACKEND_H
+#define NETHER_DUMMY_BACKEND_H
+
+
+#include "nether_PolicyBackend.h"
+
+class NetherDummyBackend : public NetherPolicyBackend
+{
+ public:
+ NetherDummyBackend(const NetherConfig &netherConfig)
+ : NetherPolicyBackend(netherConfig) {}
+ ~NetherDummyBackend() {}
+
+ const bool isValid()
+ {
+ return (true);
+ }
+
+ const bool initialize()
+ {
+ return (true);
+ }
+
+ const bool enqueueVerdict(const NetherPacket &packet)
+ {
+ return (castVerdict (packet, netherConfig.defaultVerdict));
+ }
+
+ const bool processEvents()
+ {
+ return (true);
+ }
+};
+
+#endif
--- /dev/null
+#ifndef NETHER_FILE_BACKEND_H
+#define NETHER_FILE_BACKEND_H
+
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+#include <tuple>
+
+#include "nether_PolicyBackend.h"
+
+#define NETHER_POLICY_CREDS_DELIM ":"
+
+class NetherManager;
+
+enum PolicyFileTokens
+{
+ uidT,
+ gidT,
+ secctxT,
+ verdictT
+};
+
+struct PolicyEntry
+{
+ uid_t uid;
+ gid_t gid;
+ std::string securityContext;
+ NetherVerdict verdict;
+};
+
+static const std::string dumpPolicyEntry(const PolicyEntry &entry)
+{
+ std::stringstream stream;
+ stream << "UID=";
+ if (entry.uid == NETHER_INVALID_UID) stream << "*"; else stream << entry.uid;
+ stream << " GID=";
+ if (entry.gid == NETHER_INVALID_GID) stream << "*"; else stream << entry.gid;
+ stream << " SECCTX=";
+ if (entry.securityContext.empty()) stream << "*"; else stream << entry.securityContext;
+ stream << " VERDICT=";
+ stream << verdictToString(entry.verdict);
+
+ return (stream.str());
+}
+
+class NetherFileBackend : public NetherPolicyBackend
+{
+ public:
+ NetherFileBackend(const NetherConfig &netherConfig);
+ ~NetherFileBackend();
+ const bool isValid();
+ const bool initialize();
+ const bool reload();
+ const bool enqueueVerdict(const NetherPacket &packet);
+ const bool parsePolicyFile(std::ifstream &policyFile);
+ const bool processEvents() { return (true); }
+ std::vector<std::string> split(const std::string &str, const std::string &delim);
+ private:
+ std::vector<PolicyEntry> policy;
+};
+
+#endif
--- /dev/null
+#ifndef NETHER_MANAGER_H
+#define NETHER_MANAGER_H
+
+#include "nether_Types.h"
+#include "nether_DummyBackend.h"
+#include "nether_Netlink.h"
+
+
+class NetherManager : public NetherVerdictListener, public NetherProcessedPacketListener
+{
+ public:
+ NetherManager(const NetherConfig &_netherConfig);
+ ~NetherManager();
+ const bool initialize();
+ const bool process();
+ NetherConfig &getConfig();
+ static NetherPolicyBackend *getPolicyBackend(const NetherConfig &netherConfig, const bool primary = true);
+ bool verdictCast (const u_int32_t packetId, const NetherVerdict verdict);
+ void packetReceived (const NetherPacket &packet);
+
+ private:
+ NetherPolicyBackend *netherPrimaryPolicyBackend, *netherBackupPolicyBackend;
+ NetherDummyBackend *netherFallbackPolicyBackend;
+ NetherNetlink *netherNetlink;
+ NetherConfig netherConfig;
+ int netlinkDescriptor, backendDescriptor, signalDescriptor;
+ sigset_t signalMask;
+};
+
+#endif
--- /dev/null
+#ifndef NETHER_NETLINK_H\r
+#define NETHER_NETLINK_H\r
+\r
+#include "nether_Types.h"\r
+#include "nether_Utils.h"
+\r
+class NetherManager;\r
+\r
+class NetherNetlink : public NetherPacketProcessor\r
+{\r
+ public:\r
+ NetherNetlink(NetherConfig &netherConfig);\r
+ ~NetherNetlink();\r
+ const bool initialize();
+ const bool reload();\r
+ static int callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data);
+ const bool processPacket (char *packetBuffer, const int packetReadSize);
+ void setVerdict(const u_int32_t packetId, const NetherVerdict verdict);
+ int getDescriptor();\r
+ const bool isValid();
+\r
+ protected:
+ NetherPacket *processedPacket;
+\r
+ private:\r
+ struct nfq_q_handle *queueHandle;\r
+ struct nfq_handle *nfqHandle;
+ struct nlif_handle *nlif;\r
+ int fd;\r
+ uint32_t queue;\r
+};\r
+\r
+#endif // NETLINK_H_INCLUDED\r
--- /dev/null
+#ifndef NETHER_POLICY_BACKEND_H
+#define NETHER_POLICY_BACKEND_H
+
+#include "nether_Types.h"
+#include "nether_Utils.h"
+
+class NetherPolicyBackend : public NetherVerdictCaster
+{
+ public:
+ NetherPolicyBackend(const NetherConfig &_netherConfig) : netherConfig(_netherConfig) {}
+ virtual ~NetherPolicyBackend() {}
+ virtual const bool enqueueVerdict (const NetherPacket &packet) = 0;
+ virtual const bool initialize() = 0;
+ virtual const bool reload() { return (true); };
+ virtual const bool isValid() = 0;
+ virtual const int getDescriptor() { return (-1); }
+ virtual const NetherDescriptorStatus getDescriptorStatus() { return (unknownStatus); }
+ virtual const bool processEvents() = 0;
+
+ protected:
+ NetherConfig netherConfig;
+};
+
+#endif
--- /dev/null
+#ifndef NETHER_TYPES_H
+#define NETHER_TYPES_H
+
+#include <iostream>
+#include <errno.h>
+#include <iostream>
+#include <sstream>
+#include <unistd.h>
+#include <memory>
+
+#include <sys/types.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <strings.h>
+#include <getopt.h>
+#include <assert.h>
+#include <netinet/in.h>
+#include <sys/signalfd.h>
+#include <linux/types.h>
+#include <linux/netfilter.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+#include "logger/logger.hpp"
+#include "logger/backend-file.hpp"
+#include "logger/backend-stderr.hpp"
+#include "logger/backend-syslog.hpp"
+
+#ifdef HAVE_CYNARA
+ #define NETHER_PRIMARY_BACKEND cynaraBackend
+ #define NETHER_BACKUP_BACKEND fileBackend
+#else
+ #define NETHER_PRIMARY_BACKEND fileBackend
+ #define NETHER_BACKUP_BACKEND dummyBackend
+#endif
+
+#define NETHER_DEFAULT_VERDICT allowAndLog
+#define NETHER_PACKET_BUFFER_SIZE 4096
+#define NETHER_INVALID_UID (uid_t) -1
+#define NETHER_INVALID_GID (gid_t) -1
+#define NETHER_NETWORK_ADDR_LEN 16 /* enough to hold ipv4 and ipv6 */
+#define NETHER_NETWORK_IPV4_ADDR_LEN 4
+#define NETHER_NETWORK_IPV6_ADDR_LEN 16
+#define NETHER_MAX_USER_LEN 32
+#define NETLINK_DROP_MARK 3
+#define NETLINK_ALLOWLOG_MARK 4
+#define NETHER_LOG_BACKEND stderrBackend
+
+enum NetherPolicyBackendType
+{
+ cynaraBackend,
+ fileBackend,
+ dummyBackend
+};
+
+enum NetherLogBackendType
+{
+ stderrBackend,
+ syslogBackend,
+ journalBackend,
+ logfileBackend,
+ nullBackend
+};
+
+enum NetherVerdict
+{
+ allow,
+ allowAndLog,
+ deny,
+ noVerdictYet
+};
+
+enum NetherDescriptorStatus
+{
+ readOnly,
+ writeOnly,
+ readWrite,
+ unknownStatus
+};
+
+enum NetherTransportType
+{
+ TCP,
+ UDP,
+ ICMP,
+ IGMP,
+ unknownTransportType
+};
+
+enum NetherProtocolType
+{
+ IPv4,
+ IPv6,
+ unknownProtocolType
+};
+
+
+struct NetherPacket
+{
+ u_int32_t id;
+ std::string securityContext;
+ uid_t uid;
+ gid_t gid;
+ pid_t pid;
+ NetherTransportType transportType;
+ NetherProtocolType protocolType;
+ char localAddress[NETHER_NETWORK_ADDR_LEN];
+ int localPort;
+ char remoteAddress[NETHER_NETWORK_ADDR_LEN];
+ int remotePort;
+};
+
+struct NetherConfig
+{
+ NetherVerdict defaultVerdict = NETHER_DEFAULT_VERDICT;
+ NetherPolicyBackendType primaryBackendType = NETHER_PRIMARY_BACKEND;
+ NetherPolicyBackendType backupBackendType = NETHER_BACKUP_BACKEND;
+ NetherLogBackendType logBackend = NETHER_LOG_BACKEND;
+ int primaryBackendRetries = 3;
+ int backupBackendRetries = 3;
+ int debugMode = 0;
+ int nodaemonMode = 0;
+ int queueNumber = 0;
+ std::string backupBackendArgs;
+ std::string primaryBackendArgs;
+ std::string logBackendArgs;
+ uint8_t markDeny = NETLINK_DROP_MARK;
+ uint8_t markAllowAndLog = NETLINK_ALLOWLOG_MARK;
+};
+
+class NetherVerdictListener
+{
+ public:
+ virtual bool verdictCast (const u_int32_t packetId, const NetherVerdict verdict) = 0;
+};
+
+class NetherVerdictCaster
+{
+ public:
+ NetherVerdictCaster() : verdictListener(nullptr) {}
+ virtual ~NetherVerdictCaster() {}
+
+ void setListener(NetherVerdictListener *listenerToSet)
+ {
+ verdictListener = listenerToSet;
+ }
+
+ bool castVerdict (const NetherPacket &packet, const NetherVerdict verdict)
+ {
+ if (verdictListener)
+ return (verdictListener->verdictCast(packet.id, verdict));
+ return (false);
+ }
+
+ bool castVerdict (const u_int32_t packetId, const NetherVerdict verdict)
+ {
+ if (verdictListener)
+ return (verdictListener->verdictCast(packetId, verdict));
+ return (false);
+ }
+
+ protected:
+ NetherVerdictListener *verdictListener;
+};
+
+class NetherProcessedPacketListener
+{
+ public:
+ virtual void packetReceived (const NetherPacket &packet) = 0;
+};
+
+class NetherPacketProcessor
+{
+ public:
+ NetherPacketProcessor(NetherConfig &_netherConfig) : netherConfig(_netherConfig), packetListener(nullptr) {}
+ virtual ~NetherPacketProcessor() {}
+ virtual const bool reload() { return (true); }
+ void setListener(NetherProcessedPacketListener *listenerToSet)
+ {
+ packetListener = listenerToSet;
+ }
+
+ void processNetherPacket (NetherPacket packetInfoToWrite)
+ {
+ if (packetListener) packetListener->packetReceived(packetInfoToWrite);
+ }
+
+ virtual void setVerdict(const NetherPacket &packet, const NetherVerdict verdict) {}
+ protected:
+ NetherProcessedPacketListener *packetListener;
+ NetherConfig netherConfig;
+};
+#endif
--- /dev/null
+#ifndef NETHER_UTILS_H
+#define NETHER_UTILS_H
+#include "nether_Types.h"
+void decodePacket(NetherPacket &packet, unsigned char *payload);
+void decodeIPv4Packet(NetherPacket &packet, unsigned char *payload);
+void decodeIPv6Packet(NetherPacket &packet, unsigned char *payload);
+void decodeTcp(NetherPacket &packet, unsigned char *payload);
+void decodeUdp(NetherPacket &packet, unsigned char *payload);
+const std::string ipAddressToString(const char *src, enum NetherProtocolType type);
+
+template <typename Type>
+inline void deleteAndZero (Type& pointer) { delete pointer; pointer = nullptr; }
+
+static const NetherVerdict stringToVerdict (char *verdictAsString)
+{
+ if (verdictAsString)
+ {
+ if (strncasecmp (verdictAsString, "allow_log", 9) == 0)
+ return (allowAndLog);
+ if (strncasecmp (verdictAsString, "allow", 6) == 0)
+ return (allow);
+ if (strncasecmp (verdictAsString, "deny", 4) == 0)
+ return (deny);
+ }
+ return (allowAndLog);
+}
+
+static const NetherPolicyBackendType stringToBackendType (char *backendAsString)
+{
+ if (strcasecmp (backendAsString, "cynara") == 0)
+ return (cynaraBackend);
+ if (strcasecmp (backendAsString, "file") == 0)
+ return (fileBackend);
+ if (strcasecmp (backendAsString, "dummy") == 0)
+ return (dummyBackend);
+
+ return (dummyBackend);
+}
+
+static const NetherLogBackendType stringToLogBackendType(char *backendAsString)
+{
+ if (strcasecmp (backendAsString, "stderr") == 0)
+ return (stderrBackend);
+ if (strcasecmp (backendAsString, "syslog") == 0)
+ return (syslogBackend);
+ if (strcasecmp (backendAsString, "journal") == 0)
+ return (journalBackend);
+ if (strcasecmp (backendAsString, "file") == 0)
+ return (logfileBackend);
+ if (strcasecmp (backendAsString, "null") == 0)
+ return (nullBackend);
+
+ return (nullBackend);
+}
+
+static const std::string logBackendTypeToString(const NetherLogBackendType backendType)
+{
+ switch (backendType)
+ {
+ case stderrBackend:
+ return ("stderr");
+ case syslogBackend:
+ return ("syslog");
+ case journalBackend:
+ return ("journal");
+ case logfileBackend:
+ return ("file");
+ case nullBackend:
+ return ("null");
+ }
+ return ("null");
+}
+
+static const std::string backendTypeToString (const NetherPolicyBackendType backendType)
+{
+ switch (backendType)
+ {
+ case cynaraBackend:
+ return ("cynara");
+ case fileBackend:
+ return ("file");
+ case dummyBackend:
+ default:
+ return ("dummy");
+ }
+}
+
+static const std::string verdictToString (const NetherVerdict verdict)
+{
+ switch (verdict)
+ {
+ case allow:
+ return ("ALLOW");
+ case allowAndLog:
+ return ("ALLOW_LOG");
+ case deny:
+ return ("DENY");
+ }
+}
+
+static const std::string transportToString(const NetherTransportType transportType)
+{
+ switch (transportType)
+ {
+ case TCP:
+ return ("TCP");
+ case UDP:
+ return ("UDP");
+ case ICMP:
+ return ("ICMP");
+ case IGMP:
+ return ("IGMP");
+ case unknownTransportType:
+ default:
+ return ("UNKNOWN");
+ }
+}
+
+static const std::string protocolToString(const NetherProtocolType protocolType)
+{
+ switch (protocolType)
+ {
+ case IPv4:
+ return ("IPv4");
+ case IPv6:
+ return ("IPv6");
+ default:
+ return ("UNKNOWN");
+ }
+}
+
+static const std::string packetToString (const NetherPacket &packet)
+{
+ std::stringstream stream;
+ stream << "ID=";
+ stream << packet.id;
+ stream << " SECCTX=";
+ stream << packet.securityContext;
+ stream << " UID=";
+ stream << packet.uid;
+ stream << " PROTO=";
+ stream << protocolToString(packet.protocolType);
+ stream << " TRANSPORT=";
+ stream << transportToString(packet.transportType);
+ stream << " SADDR=";
+ stream << ipAddressToString(&packet.localAddress[0], packet.protocolType);
+ stream << ":";
+ stream << packet.localPort;
+ stream << " DADDR=";
+ stream << ipAddressToString(&packet.remoteAddress[0], packet.protocolType);
+ stream << ":";
+ stream << packet.remotePort;
+ return (stream.str());
+}
+
+template<typename ... Args>
+std::string stringFormat( const char* format, Args ... args )
+{
+ size_t size = snprintf( nullptr, 0, format, args ... ) + 1; // Extra space for '\0'
+ std::unique_ptr<char[]> buf( new char[ size ] );
+ snprintf( buf.get(), size, format, args ... );
+ return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
+}
+#endif
--- /dev/null
+#include "nether_CynaraBackend.h"
+
+// #ifdef HAVE_CYNARA
+
+NetherCynaraBackend::NetherCynaraBackend(const NetherConfig &netherConfig)
+ : NetherPolicyBackend(netherConfig), currentCynaraDescriptor(0),
+ cynaraLastResult(CYNARA_API_UNKNOWN_ERROR)
+{
+ responseQueue.reserve(1024);
+}
+
+NetherCynaraBackend::~NetherCynaraBackend()
+{
+}
+
+const bool NetherCynaraBackend::initialize()
+{
+ cynaraLastResult = cynara_async_initialize(&cynaraContext, NULL, &statusCallback, this);
+ if (cynaraLastResult != CYNARA_API_SUCCESS)
+ {
+ LOGE("Failed to initialize cynara client " << cynaraErrorCodeToString(cynaraLastResult));
+ return (false);
+ }
+
+ return (true);
+}
+
+void NetherCynaraBackend::statusCallback(int oldFd, int newFd, cynara_async_status status, void *data)
+{
+ LOGD("oldFd=" << oldFd << "newFd=" << newFd);
+
+ NetherCynaraBackend *backend = static_cast<NetherCynaraBackend *>(data);
+
+ if (status == CYNARA_STATUS_FOR_READ)
+ backend->setCynaraDescriptor(newFd, readOnly);
+
+ if (status == CYNARA_STATUS_FOR_RW)
+ backend->setCynaraDescriptor(newFd, readWrite);
+}
+
+void NetherCynaraBackend::checkCallback(cynara_check_id check_id,
+ cynara_async_call_cause cause,
+ int response,
+ void *data)
+{
+ NetherCynaraBackend *backend = static_cast<NetherCynaraBackend *>(data);
+
+ if (cause == CYNARA_CALL_CAUSE_ANSWER)
+ backend->setCynaraVerdict (check_id, response);
+ else
+ LOGI("unknown reason for call cause="<< cause <<" response="<< response);
+}
+
+const bool NetherCynaraBackend::enqueueVerdict (const NetherPacket &packet)
+{
+ char user[NETHER_MAX_USER_LEN];
+ cynara_check_id checkId;
+
+ snprintf (user, sizeof(user), "%du", packet.uid);
+
+ cynaraLastResult = cynara_async_check_cache(cynaraContext, packet.securityContext.c_str(), "", user, NETHER_CYNARA_INTERNET_PRIVILEGE);
+
+ switch (cynaraLastResult)
+ {
+ case CYNARA_API_ACCESS_ALLOWED:
+ LOGD(cynaraErrorCodeToString(cynaraLastResult).c_str());
+ return (castVerdict(packet, allow));
+
+ case CYNARA_API_ACCESS_DENIED:
+ LOGD(cynaraErrorCodeToString(cynaraLastResult).c_str());
+ return (castVerdict(packet, deny));
+
+ case CYNARA_API_CACHE_MISS:
+ cynaraLastResult = cynara_async_create_request(cynaraContext,
+ packet.securityContext.c_str(),
+ "",
+ user,
+ NETHER_CYNARA_INTERNET_PRIVILEGE,
+ &checkId,
+ &checkCallback,
+ this);
+ if (cynaraLastResult == CYNARA_API_SUCCESS)
+ {
+ responseQueue.insert (responseQueue.begin() + checkId, packet.id);
+
+ return (true);
+ }
+ else if (cynaraLastResult == CYNARA_API_SERVICE_NOT_AVAILABLE)
+ {
+ LOGW("Cynara offline, fall back to another backend");
+ return (false);
+ }
+ else
+ {
+ LOGW("Error on cynara request create after CYNARA_API_CACHE_MISS " << cynaraErrorCodeToString(cynaraLastResult));
+ return (false);
+ }
+
+ default:
+ LOGW("Error on cynara request create unhandled result from cynara_async_check_cache "<<cynaraErrorCodeToString(cynaraLastResult));
+ return (false);
+ }
+
+ return (true);
+}
+
+void NetherCynaraBackend::setCynaraVerdict(cynara_check_id checkId, int cynaraResult)
+{
+ u_int32_t packetId = 0;
+ if ((packetId = responseQueue.at(checkId)) >= 0)
+ {
+ responseQueue.erase(responseQueue.begin() + checkId);
+
+ if (cynaraResult == CYNARA_API_ACCESS_ALLOWED)
+ castVerdict (packetId, allow);
+ else
+ castVerdict (packetId, deny);
+
+ return;
+ }
+
+ LOGW("checkId=" << checkId << " has no assosiated packetId");
+}
+
+const bool NetherCynaraBackend::isValid()
+{
+ return ((cynaraLastResult == CYNARA_API_SUCCESS ? true : false) && cynaraContext);
+}
+
+const int NetherCynaraBackend::getDescriptor()
+{
+ return (currentCynaraDescriptor);
+}
+
+const NetherDescriptorStatus NetherCynaraBackend::getDescriptorStatus()
+{
+ return (currentCynaraDescriptorStatus);
+}
+
+void NetherCynaraBackend::setCynaraDescriptor(const int _currentCynaraDescriptor, const NetherDescriptorStatus _currentCynaraDescriptorStatus)
+{
+ currentCynaraDescriptorStatus = _currentCynaraDescriptorStatus;
+ currentCynaraDescriptor = _currentCynaraDescriptor;
+}
+
+const bool NetherCynaraBackend::processEvents()
+{
+ int ret = cynara_async_process(cynaraContext);
+
+ if (ret == CYNARA_API_SUCCESS)
+ return (true);
+
+ LOGW("cynara_async_process failed " << cynaraErrorCodeToString(ret));
+ return (false);
+}
+//#endif
--- /dev/null
+#include "nether_FileBackend.h"
+
+NetherFileBackend::NetherFileBackend (const NetherConfig &netherConfig)
+ : NetherPolicyBackend(netherConfig)
+{
+}
+
+NetherFileBackend::~NetherFileBackend()
+{
+}
+
+const bool NetherFileBackend::isValid()
+{
+ return (true);
+}
+
+const bool NetherFileBackend::initialize()
+{
+ std::ifstream policyFile;
+ policyFile.open (netherConfig.backupBackendArgs, std::ifstream::in);
+
+ if (!policyFile)
+ {
+ LOGE("Can't open policy file at: " << netherConfig.backupBackendArgs);
+ return (false);
+ }
+
+ return (parsePolicyFile(policyFile));
+}
+
+const bool NetherFileBackend::reload()
+{
+ return (initialize());
+}
+
+const bool NetherFileBackend::enqueueVerdict(const NetherPacket &packet)
+{
+ for (auto &policyIterator : policy)
+ {
+ if (
+ ( (policyIterator.uid == packet.uid) || policyIterator.uid == NETHER_INVALID_UID ) &&
+ ( (policyIterator.gid == packet.gid) || policyIterator.gid == NETHER_INVALID_GID ) &&
+ ( (policyIterator.securityContext == packet.securityContext) || policyIterator.securityContext.empty() )
+ )
+ {
+ LOGD("policy match " << dumpPolicyEntry(policyIterator));
+ return (castVerdict(packet, policyIterator.verdict));
+ }
+ }
+
+ return (false);
+}
+
+const bool NetherFileBackend::parsePolicyFile(std::ifstream &policyFile)
+{
+ std::string line;
+ std::vector<std::string> tokens;
+ policy.clear();
+
+ while (!policyFile.eof())
+ {
+ getline(policyFile, line);
+ if (line[0] == '#' || line.empty() || !line.find(NETHER_POLICY_CREDS_DELIM, 0))
+ continue;
+
+ tokens = split (line, NETHER_POLICY_CREDS_DELIM);
+
+ if (tokens.size() > 0)
+ {
+ PolicyEntry entry { tokens[uidT].empty() ? NETHER_INVALID_UID : (uid_t)strtol(tokens[uidT].c_str(), NULL, 10), /* uid */
+ tokens[gidT].empty() ? NETHER_INVALID_GID : (gid_t)strtol(tokens[gidT].c_str(), NULL, 10), /* gid */
+ tokens[secctxT], /* security context */
+ stringToVerdict((char *)tokens[verdictT].c_str()) /* verdict */
+ };
+
+ LOGD("\t"<<dumpPolicyEntry(entry).c_str());
+ policy.push_back(entry);
+ }
+ }
+
+ return (true);
+}
+
+std::vector<std::string> NetherFileBackend::split(const std::string &str, const std::string &delim)
+{
+ std::vector<std::string> tokens;
+ size_t start = 0, end = 0;
+
+ while (end != std::string::npos)
+ {
+ end = str.find(delim, start);
+
+ // If at end, use length=maxLength. Else use length=end-start.
+ tokens.push_back(str.substr(start, (end == std::string::npos) ? std::string::npos : end - start));
+
+ // If at end, use start=maxSize. Else use start=end+delimiter.
+ start = ((end > (std::string::npos - delim.size())) ? std::string::npos : end + delim.size());
+ }
+
+ return (tokens);
+}
--- /dev/null
+#include "nether_Types.h"
+#include "nether_Utils.h"
+#include "nether_Manager.h"
+
+using namespace std;
+void showHelp(char *arg);
+
+int main(int argc, char *argv[])
+{
+ int optionIndex, c;
+ struct NetherConfig netherConfig;
+
+ static struct option longOptions[] =
+ {
+ {"nodaemon", no_argument, &netherConfig.nodaemonMode, 1},
+ {"log", required_argument, 0, 'l'},
+ {"log-args", required_argument, 0, 'L'},
+ {"default-verdict", required_argument, 0, 'V'},
+ {"primary-backend", required_argument, 0, 'p'},
+ {"primary-backend-args", required_argument, 0, 'P'},
+ {"backup-backend", required_argument, 0, 'b'},
+ {"backup-backend-args", required_argument, 0, 'B'},
+ {"queue-num", required_argument, 0, 'q'},
+ {"mark-deny", required_argument, 0, 'm'},
+ {"mark-allow-log", required_argument, 0, 'M'},
+ {"help", no_argument, 0, 'h'},
+ {0, 0, 0, 0}
+ };
+
+ while (1)
+ {
+ c = getopt_long (argc, argv, ":nl:L:V:p:P:b:B:q:m:M:h", longOptions, &optionIndex);
+
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ break;
+
+ case 'n':
+ netherConfig.nodaemonMode = 1;
+ break;
+
+ case 'l':
+ netherConfig.logBackend = stringToLogBackendType(optarg);
+ break;
+
+ case 'L':
+ netherConfig.logBackendArgs = optarg;
+ break;
+
+ case 'V':
+ netherConfig.defaultVerdict = stringToVerdict (optarg);
+ break;
+
+ case 'p':
+ netherConfig.primaryBackendType = stringToBackendType (optarg);
+ break;
+
+ case 'P':
+ netherConfig.primaryBackendArgs = optarg;
+ break;
+
+ case 'b':
+ netherConfig.backupBackendType = stringToBackendType (optarg);
+ break;
+
+ case 'B':
+ netherConfig.backupBackendArgs = optarg;
+ break;
+
+ case 'q':
+ if (atoi(optarg) < 0 || atoi(optarg) >= 65535)
+ {
+ cerr << "Queue number is invalid (must be >= 0 and < 65535): " << atoi(optarg);
+ exit (1);
+ }
+ netherConfig.queueNumber = atoi(optarg);
+ break;
+
+ case 'm':
+ if (atoi(optarg) <= 0 || atoi(optarg) >= 255)
+ {
+ cerr << "Packet mark for DENY is invalid (must be > 0 and < 255): " << atoi(optarg);
+ exit (1);
+ }
+ netherConfig.markDeny = atoi(optarg);
+ break;
+
+ case 'M':
+ if (atoi(optarg) <= 0 || atoi(optarg) >= 255)
+ {
+ cerr << "Packet mark for ALLOW_LOG is invalid (must be > 0 and < 255): " << atoi(optarg);
+ exit (1);
+ }
+ netherConfig.markAllowAndLog = atoi(optarg);
+ break;
+
+ case 'h':
+ showHelp (argv[0]);
+ exit (1);
+ }
+ }
+ switch (netherConfig.logBackend)
+ {
+ case stderrBackend:
+ logger::Logger::setLogBackend (new logger::StderrBackend(false));
+ break;
+ case syslogBackend:
+ logger::Logger::setLogBackend (new logger::SyslogBackend());
+ break;
+ case logfileBackend:
+ logger::Logger::setLogBackend (new logger::FileBackend(netherConfig.logBackendArgs));
+ break;
+ default:
+ logger::Logger::setLogBackend (new logger::StderrBackend(false));
+ break;
+ }
+
+ LOGD("NETHER OPTIONS:"
+#if defined(_DEBUG)
+ << " debug"
+#endif
+ << " nodaemon=" << netherConfig.nodaemonMode
+ << " queue=" << netherConfig.queueNumber);
+ LOGD("primary-backend=" << backendTypeToString (netherConfig.primaryBackendType)
+ << " primary-backend-args=" << netherConfig.primaryBackendArgs);
+ LOGD("backup-backend=" << backendTypeToString (netherConfig.backupBackendType)
+ << " backup-backend-args=" << netherConfig.backupBackendArgs);
+ LOGD("default-verdict=" << verdictToString(netherConfig.defaultVerdict)
+ << " mark-deny=" << (int)netherConfig.markDeny
+ << " mark-allow-log=" << (int)netherConfig.markAllowAndLog);
+ LOGD("log-backend=" << logBackendTypeToString(netherConfig.logBackend)
+ << " log-backend-args=" << netherConfig.logBackendArgs);
+
+ NetherManager manager (netherConfig);
+
+ if (!manager.initialize())
+ {
+ LOGE("NetherManager failed to initialize, exiting");
+ return (1);
+ }
+
+ manager.process();
+
+ return (0);
+}
+
+void showHelp(char *arg)
+{
+ cout<< "Usage:\t"<< arg << " [OPTIONS]\n\n";
+ cout<< " -n,--nodaemon\t\t\t\tDon't run as daemon in the background\n";
+ cout<< " -d,--debug\t\t\t\tRun in debug mode (implies --nodaemon)\n";
+ cout<< " -l,--log=<backend>\t\t\tSet logging backend STDERR,SYSLOG,JOURNAL (default:"<< logBackendTypeToString(NETHER_LOG_BACKEND) << ")\n";
+ cout<< " -L,--log-args=<arguments>\t\tSet logging backend arguments\n";
+ cout<< " -V,--verdict=<verdict>\t\tWhat verdict to cast when policy backend is not available\n\t\t\t\t\tACCEPT,ALLOW_LOG,DENY (default:"<<verdictToString(NETHER_DEFAULT_VERDICT)<<")\n";
+ cout<< " -p,--primary-backend=<module>\t\tPrimary policy backend\n\t\t\t\t\tCYNARA,FILE,NONE (defualt:"<< backendTypeToString(NETHER_PRIMARY_BACKEND)<<")\n";
+ cout<< " -P,--primary-backend-args=<arguments>\tPrimary policy backend arguments\n";
+ cout<< " -b,--backup-backend=<module>\t\tBackup policy backend\n\t\t\t\t\tCYNARA,FILE,NONE (defualt:"<< backendTypeToString(NETHER_BACKUP_BACKEND)<< ")\n";
+ cout<< " -B,--backup-backend-args=<arguments>\tBackup policy backend arguments\n";
+ cout<< " -q,--queue-num=<queue number>\t\tNFQUEUE queue number to use for receiving packets\n";
+ cout<< " -m,--mark-deny=<mark>\t\t\tPacket mark to use for DENY verdicts (default:"<< NETLINK_DROP_MARK << ")\n";
+ cout<< " -M,--mark-allow-log=<mark>\t\tPacket mark to use for ALLOW_LOG verdicts (default:" << NETLINK_ALLOWLOG_MARK << ")\n";
+ cout<< " -h,--help\t\t\t\tshow help information\n";
+}
--- /dev/null
+#include "nether_Manager.h"
+#include "nether_CynaraBackend.h"
+#include "nether_FileBackend.h"
+#include "nether_DummyBackend.h"
+
+NetherManager::NetherManager(const NetherConfig &_netherConfig)
+ : netherConfig(_netherConfig),
+ netherPrimaryPolicyBackend(nullptr),
+ netherBackupPolicyBackend(nullptr),
+ netherFallbackPolicyBackend(nullptr)
+{
+ netherNetlink = new NetherNetlink(netherConfig);
+ netherNetlink->setListener (this);
+
+ netherPrimaryPolicyBackend = getPolicyBackend (netherConfig);
+ netherPrimaryPolicyBackend->setListener (this);
+
+ netherBackupPolicyBackend = getPolicyBackend (netherConfig, false);
+ netherBackupPolicyBackend->setListener (this);
+
+ netherFallbackPolicyBackend = new NetherDummyBackend(netherConfig);
+}
+
+NetherManager::~NetherManager()
+{
+ deleteAndZero (netherPrimaryPolicyBackend);
+ deleteAndZero (netherBackupPolicyBackend);
+ deleteAndZero (netherFallbackPolicyBackend);
+ deleteAndZero (netherNetlink);
+ close (signalDescriptor);
+}
+
+const bool NetherManager::initialize()
+{
+ sigemptyset(&signalMask);
+ sigaddset(&signalMask, SIGHUP);
+
+ if (sigprocmask(SIG_BLOCK, &signalMask, NULL) == -1)
+ {
+ LOGE("Failed to block signals sigprocmask()");
+ return (false);
+ }
+
+ signalDescriptor = signalfd(-1, &signalMask, 0);
+ if (signalDescriptor == -1)
+ {
+ LOGE("Failed acquire signalfd descriptor");
+ return (false);
+ }
+
+ if (!netherNetlink->initialize())
+ {
+ LOGE("Failed to initialize netlink subsystem, exiting");
+ return (false);
+ }
+
+ if (!netherPrimaryPolicyBackend->initialize())
+ {
+ LOGE("Failed to initialize primary policy backend, exiting");
+ return (false);
+ }
+
+ if (!netherBackupPolicyBackend->initialize())
+ {
+ LOGE("Failed to initialize backup backend, exiting");
+ return (false);
+ }
+
+ if ((netlinkDescriptor = netherNetlink->getDescriptor()) == -1)
+ {
+ LOGE("Netlink subsystem did not return a valid descriptor, exiting");
+ return (false);
+ }
+
+ if ((backendDescriptor = netherPrimaryPolicyBackend->getDescriptor()) == -1)
+ {
+ LOGI("Policy backend does not provide descriptor for select()");
+ }
+ return (true);
+}
+
+const bool NetherManager::process()
+{
+ NetherPacket receivedPacket;
+ int packetReadSize;
+ ssize_t signalRead;
+ struct signalfd_siginfo signalfdSignalInfo;\r
+ fd_set watchedReadDescriptorsSet, watchedWriteDescriptorsSet;\r
+ struct timeval timeoutSpecification;\r
+ char packetBuffer[NETHER_PACKET_BUFFER_SIZE] __attribute__ ((aligned));\r
+\r
+ while (1)\r
+ {\r
+ FD_ZERO (&watchedReadDescriptorsSet);
+ FD_ZERO (&watchedWriteDescriptorsSet);
+
+ /* Always listen for signals */
+ FD_SET (signalDescriptor, &watchedReadDescriptorsSet);
+
+ if ((netlinkDescriptor = netherNetlink->getDescriptor()) >= 0)
+ {\r
+ FD_SET(netlinkDescriptor, &watchedReadDescriptorsSet);
+ }
+
+ if ((backendDescriptor = netherPrimaryPolicyBackend->getDescriptor()) >= 0)
+ {
+ if (netherPrimaryPolicyBackend->getDescriptorStatus() == readOnly)
+ {
+ FD_SET(backendDescriptor, &watchedReadDescriptorsSet);
+ }
+ else if (netherPrimaryPolicyBackend->getDescriptorStatus() == readWrite)
+ {
+ FD_SET(backendDescriptor, &watchedReadDescriptorsSet);
+ FD_SET(backendDescriptor, &watchedWriteDescriptorsSet);
+ }
+ }
+\r
+ timeoutSpecification.tv_sec = 240;\r
+ timeoutSpecification.tv_usec = 0;
+\r
+ if (select (FD_SETSIZE, &watchedReadDescriptorsSet, &watchedWriteDescriptorsSet, NULL, &timeoutSpecification) < 0)\r
+ {\r
+ LOGE("select error " << strerror(errno));\r
+ return (false);\r
+ }\r
+
+ if (FD_ISSET(signalDescriptor, &watchedReadDescriptorsSet))
+ {
+ LOGD("received signal");
+ signalRead = read (signalDescriptor, &signalfdSignalInfo, sizeof(struct signalfd_siginfo));
+
+ if (signalRead != sizeof(struct signalfd_siginfo))
+ {
+ LOGW("Received incomplete signal information, ignore");
+ continue;
+ }
+
+ if (signalfdSignalInfo.ssi_signo == SIGHUP)
+ {
+ LOGI("SIGHUP received, reloading");
+ if (!netherPrimaryPolicyBackend->reload())
+ LOGW("primary backend failed to reload");
+ if (!netherBackupPolicyBackend->reload())
+ LOGW("backup backend failed to reload");
+ if (!netherNetlink->reload())
+ LOGW("netlink failed to reload");
+ continue;
+ }
+ }\r
+ if (FD_ISSET(netlinkDescriptor, &watchedReadDescriptorsSet))\r
+ {
+ LOGD("netlink descriptor active");
+
+ /* some data arrives on netlink, read it */\r
+ if ((packetReadSize = recv(netlinkDescriptor, packetBuffer, sizeof(packetBuffer), 0)) >= 0)\r
+ {
+ /* try to process the packet using netfilter_queue library, fetch packet info
+ needed for making a decision about it */\r
+ if (netherNetlink->processPacket (packetBuffer, packetReadSize))
+ {
+ continue;
+ }
+ else
+ {
+ /* if we can't process the incoming packets, it's bad. Let's exit now */
+ LOGE("Failed to process netlink received packet, refusing to continue");
+ break;
+ }\r
+ }
+\r
+ if (packetReadSize < 0 && errno == ENOBUFS)\r
+ {\r
+ LOGI("NetherManager::process losing packets! [bad things might happen]");\r
+ continue;\r
+ }\r
+\r
+ LOGE("NetherManager::process recv failed " << strerror(errno));\r
+ break;\r
+ }
+ else if (FD_ISSET(backendDescriptor, &watchedReadDescriptorsSet) || FD_ISSET(backendDescriptor, &watchedWriteDescriptorsSet))
+ {
+ LOGD("policy backend descriptor active");
+ netherPrimaryPolicyBackend->processEvents();
+ }
+ else
+ {
+ LOGD("select() timeout");
+ }
+ }
+}
+
+NetherConfig &NetherManager::getConfig()
+{
+ return (netherConfig);
+}
+
+NetherPolicyBackend *NetherManager::getPolicyBackend(const NetherConfig &netherConfig, const bool primary)
+{
+ switch (primary ? netherConfig.primaryBackendType : netherConfig.backupBackendType)
+ {
+ case cynaraBackend:
+#ifdef HAVE_CYNARA
+ return new NetherCynaraBackend(netherConfig);
+#else
+ return new NetherDummyBackend(netherConfig);
+#endif
+ case fileBackend:
+ return new NetherFileBackend(netherConfig);
+ case dummyBackend:
+ default:
+ return new NetherDummyBackend(netherConfig);
+ }
+}
+
+bool NetherManager::verdictCast (const u_int32_t packetId, const NetherVerdict verdict)
+{
+ if (netherNetlink)
+ {
+ netherNetlink->setVerdict(packetId, verdict);
+ }
+ else
+ {
+ LOGE("Netlink subsystem is invalid, can't decide on packet");
+ return (false);
+ }
+
+ return (true);
+}
+
+void NetherManager::packetReceived (const NetherPacket &packet)
+{
+ LOGD(packetToString(packet).c_str());
+
+ if (netherPrimaryPolicyBackend && netherPrimaryPolicyBackend->enqueueVerdict (packet))
+ {
+ LOGD("Primary policy accepted packet");
+ return;
+ }
+
+ if (netherBackupPolicyBackend && netherBackupPolicyBackend->enqueueVerdict (packet))
+ {
+ LOGI("Primary policy backend failed, using backup policy backend");
+ return;
+ }
+
+ /* In this situation no policy backend wants to deal with this packet
+ there propably isn't any rule in either of them
+
+ we need to make a generic decision based on whatever is hard-coded
+ or passed as a parameter to the service */
+ LOGW("All policy backends failed, using DUMMY backend");
+ netherFallbackPolicyBackend->enqueueVerdict (packet);
+}
--- /dev/null
+#include "nether_Netlink.h"\r
+\r
+NetherNetlink::NetherNetlink(NetherConfig &netherConfig)\r
+ : nfqHandle(nullptr), queueHandle(nullptr), nlif(nullptr),
+ queue(netherConfig.queueNumber),
+ NetherPacketProcessor(netherConfig)\r
+{\r
+}\r
+\r
+NetherNetlink::~NetherNetlink()\r
+{
+ if (queueHandle) nfq_destroy_queue(queueHandle);\r
+ if (nfqHandle) nfq_close(nfqHandle);\r
+}\r
+\r
+const bool NetherNetlink::initialize()\r
+{\r
+ nfqHandle = nfq_open();\r
+\r
+ if (!nfqHandle)\r
+ {\r
+ LOGE("Error during nfq_open()");\r
+ return (false);\r
+ }\r
+\r
+ if (nfq_unbind_pf(nfqHandle, AF_INET) < 0)\r
+ {\r
+ LOGE("Error during nfq_unbind_pf() (no permission?)");\r
+ return (false);\r
+ }\r
+\r
+ if (nfq_bind_pf(nfqHandle, AF_INET) < 0)\r
+ {\r
+ LOGE("Error during nfq_bind_pf()");\r
+ return (false);\r
+ }\r
+\r
+ queueHandle = nfq_create_queue(nfqHandle, queue, &callback, this);
+\r
+ if (!queueHandle)\r
+ {\r
+ LOGE("Error during nfq_create_queue()");\r
+ return (false);\r
+ }\r
+
+ if (nfq_set_queue_flags(queueHandle, NFQA_CFG_F_SECCTX, NFQA_CFG_F_SECCTX))\r
+ LOGI("This kernel version does not allow to retrieve security context");
+\r
+ if (nfq_set_mode(queueHandle, NFQNL_COPY_PACKET, 0xffff) < 0)\r
+ {\r
+ LOGE("Can't set packet_copy mode");\r
+ nfq_destroy_queue (queueHandle);\r
+ return (false);\r
+ }\r
+\r
+ if (nfq_set_queue_flags(queueHandle, NFQA_CFG_F_UID_GID, NFQA_CFG_F_UID_GID))\r
+ {\r
+ LOGE("This kernel version does not allow to retrieve process UID/GID");\r
+ nfq_destroy_queue (queueHandle);\r
+ return (false);\r
+ }\r
+
+ nlif = nlif_open();
+ if (!nlif)
+ LOGI("Failed to initialize NLIF subsystem, interface information won't be available");
+\r
+ return (true);\r
+}\r
+
+int NetherNetlink::getDescriptor()
+{
+ if (nfqHandle)
+ return (nfq_fd(nfqHandle));
+ else
+ LOGE("nfq not initialized");
+}
+
+const bool NetherNetlink::processPacket (char *packetBuffer, const int packetReadSize)
+{
+ if (nfq_handle_packet (nfqHandle, packetBuffer, packetReadSize))
+ {
+ LOGE("nfq_handle_packet failed");
+ return (false);
+ }
+
+ return (true);
+}
+\r
+int NetherNetlink::callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data)\r
+{
+ NetherNetlink *me = static_cast<NetherNetlink *>(data);
+ NetherPacket packet;
+ unsigned char *secctx;
+ int secctxSize = 0;
+ struct nfqnl_msg_packet_hdr *ph;
+ unsigned char *payload;
+\r
+ if ((ph = nfq_get_msg_packet_hdr(nfa)))
+ packet.id = ntohl(ph->packet_id);
+ else
+ {
+ LOGI("Failed to get packet id");
+ return (1);
+ }
+\r
+ if (!nfq_get_uid(nfa, &packet.uid))
+ LOGW("Failed to get uid for packet id=" << packet.id);
+\r
+ nfq_get_gid(nfa, &packet.gid);
+\r
+ secctxSize = nfq_get_secctx(nfa, &secctx);
+
+ if (secctxSize > 0)
+ packet.securityContext = std::string ((char *)secctx, secctxSize);
+ else
+ LOGD("Failed to get security context for packet id=" << packet.id);
+
+ if (nfq_get_payload(nfa, &payload) > 0)
+ decodePacket(packet, payload);
+
+ me->processNetherPacket (packet); /* this call if from the NetherPacketProcessor class */\r
+\r
+ return (0);\r
+}\r
+\r
+const bool NetherNetlink::isValid()\r
+{\r
+ return (nfqHandle && queueHandle);\r
+}
+
+void NetherNetlink::setVerdict(const u_int32_t packetId, const NetherVerdict verdict)
+{
+ int ret = 0;
+ LOGD("id=" << packetId << " verdict=" << verdictToString(verdict));
+
+ if (verdict == allow)
+ ret = nfq_set_verdict (queueHandle, packetId, NF_ACCEPT, 0, NULL);
+ if (verdict == deny)
+ ret = nfq_set_verdict2 (queueHandle, packetId, NF_ACCEPT, netherConfig.markDeny, 0, NULL);
+ if (verdict == allowAndLog)
+ ret = nfq_set_verdict2 (queueHandle, packetId, NF_ACCEPT, netherConfig.markAllowAndLog, 0, NULL);
+
+ if (ret == -1)
+ LOGW("can't set verdict for packetId=" << packetId);
+}
+
+const bool NetherNetlink::reload()
+{
+ return (true);
+}
--- /dev/null
+#include <netdb.h>
+#include <linux/types.h>
+#include "nether_Utils.h"
+
+#define IP_PROTOCOL_UDP (0x11)
+#define IP_PROTOCOL_TCP (0x06)
+#define IP_PROTOCOL_ICMP (0x01)
+#define IP_PROTOCOL_IGMP (0x02)
+#define IP_PROTOCOL_IPV6_ROUTE (0x2b)
+#define IP_PROTOCOL_IPV6_FRAG (0x2c)
+#define IP_PROTOCOL_IPV6_ICMP (0x3a)
+#define IP_PROTOCOL_IPV6_NONXT (0x3b)
+#define IP_PROTOCOL_IPV6_OPTS (0x3c)
+
+void decodePacket(NetherPacket &packet, unsigned char *payload)
+{
+ uint8_t ip_version = (payload[0] >> 4) & 0x0F;
+
+ switch(ip_version)
+ {
+ case 4:
+ packet.protocolType = IPv4;
+ decodeIPv4Packet(packet, payload);
+ break;
+ case 6:
+ packet.protocolType = IPv6;
+ decodeIPv6Packet(packet, payload);
+ break;
+ default:
+ packet.transportType = unknownTransportType;
+ packet.protocolType = unknownProtocolType;
+ break;
+ }
+}
+
+void decodeIPv6Packet(NetherPacket &packet, unsigned char *payload)
+{
+ const uint16_t start_of_ip_payload = 40;
+ uint8_t next_proto;
+
+ memcpy(packet.localAddress, &payload[8], NETHER_NETWORK_IPV6_ADDR_LEN);
+ memcpy(packet.remoteAddress, &payload[24], NETHER_NETWORK_IPV6_ADDR_LEN);
+
+ next_proto = payload[6];
+
+ switch(next_proto)
+ {
+ case IP_PROTOCOL_UDP:
+ packet.transportType = UDP;
+ decodeUdp(packet, &payload[start_of_ip_payload]);
+ break;
+ case IP_PROTOCOL_TCP:
+ packet.transportType = TCP;
+ decodeTcp(packet, &payload[start_of_ip_payload]);
+ break;
+ case IP_PROTOCOL_ICMP:
+ packet.transportType = ICMP;
+ break;
+ case IP_PROTOCOL_IGMP:
+ packet.transportType = IGMP;
+ break;
+ default:
+ packet.transportType = unknownTransportType;
+ break;
+ }
+}
+
+void decodeIPv4Packet(NetherPacket &packet, unsigned char *payload)
+{
+ uint16_t start_of_ip_payload = 0;
+ uint8_t next_proto;
+
+ start_of_ip_payload = (payload[0]&0x0F) << 2;
+
+ memcpy(packet.localAddress, &payload[12], NETHER_NETWORK_IPV4_ADDR_LEN);
+ memcpy(packet.remoteAddress, &payload[16], NETHER_NETWORK_IPV4_ADDR_LEN);
+
+ next_proto = payload[9];
+ switch(next_proto)
+ {
+ case IP_PROTOCOL_UDP:
+ packet.transportType = UDP;
+ decodeUdp(packet, &payload[start_of_ip_payload]);
+ break;
+ case IP_PROTOCOL_TCP:
+ packet.transportType = TCP;
+ decodeTcp(packet, &payload[start_of_ip_payload]);
+ break;
+ case IP_PROTOCOL_ICMP:
+ packet.transportType = ICMP;
+ break;
+ case IP_PROTOCOL_IGMP:
+ packet.transportType = IGMP;
+ default:
+ packet.transportType = unknownTransportType;
+ break;
+ }
+}
+
+void decodeTcp(NetherPacket &packet, unsigned char *payload)
+{
+ packet.localPort = ntohs(*(unsigned short*) &payload[0]);
+ packet.remotePort = ntohs(*(unsigned short*) &payload[2]);
+}
+
+void decodeUdp(NetherPacket &packet, unsigned char *payload)
+{
+ packet.localPort = ntohs(*(unsigned short*) &payload[0]);
+ packet.remotePort = ntohs(*(unsigned short*) &payload[2]);
+
+}
+
+const std::string ipAddressToString(const char *src, enum NetherProtocolType type)
+{
+ switch(type)
+ {
+ case IPv4:
+ return (stringFormat("%u.%u.%u.%u", src[0]&0xff,src[1]&0xff,src[2]&0xff,src[3]&0xff));
+ case IPv6:
+ return (stringFormat("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
+ ntohs(*(uint16_t*) &src[0]), ntohs(*(uint16_t*) &src[2]),
+ ntohs(*(uint16_t*) &src[4]), ntohs(*(uint16_t*) &src[6]),
+ ntohs(*(uint16_t*) &src[8]), ntohs(*(uint16_t*) &src[10]),
+ ntohs(*(uint16_t*) &src[12]), ntohs(*(uint16_t*) &src[14])));
+ default:
+ return ("(unknown)");
+ }
+}