From 285473ef6cb9249844e31377bb1c71ce3ee2a655 Mon Sep 17 00:00:00 2001 From: RomanKubiak Date: Thu, 16 Jul 2015 16:54:22 +0200 Subject: [PATCH 2/2] Initial source code for nether 0.0.1 (source code only) Change-Id: I16970c3dedd9071c970523a478fbf35e009d13ef --- include/nether_CynaraBackend.h | 50 ++++++++ include/nether_DummyBackend.h | 35 ++++++ include/nether_FileBackend.h | 63 ++++++++++ include/nether_Manager.h | 30 +++++ include/nether_Netlink.h | 33 ++++++ include/nether_PolicyBackend.h | 24 ++++ include/nether_Types.h | 192 +++++++++++++++++++++++++++++++ include/nether_Utils.h | 164 ++++++++++++++++++++++++++ src/nether_CynaraBackend.cpp | 156 +++++++++++++++++++++++++ src/nether_FileBackend.cpp | 101 ++++++++++++++++ src/nether_Main.cpp | 167 +++++++++++++++++++++++++++ src/nether_Manager.cpp | 253 +++++++++++++++++++++++++++++++++++++++++ src/nether_Netlink.cpp | 150 ++++++++++++++++++++++++ src/nether_NetworkUtils.cpp | 128 +++++++++++++++++++++ 14 files changed, 1546 insertions(+) create mode 100644 include/nether_CynaraBackend.h create mode 100644 include/nether_DummyBackend.h create mode 100644 include/nether_FileBackend.h create mode 100644 include/nether_Manager.h create mode 100644 include/nether_Netlink.h create mode 100644 include/nether_PolicyBackend.h create mode 100644 include/nether_Types.h create mode 100644 include/nether_Utils.h create mode 100644 src/nether_CynaraBackend.cpp create mode 100644 src/nether_FileBackend.cpp create mode 100644 src/nether_Main.cpp create mode 100644 src/nether_Manager.cpp create mode 100644 src/nether_Netlink.cpp create mode 100644 src/nether_NetworkUtils.cpp diff --git a/include/nether_CynaraBackend.h b/include/nether_CynaraBackend.h new file mode 100644 index 0000000..1242797 --- /dev/null +++ b/include/nether_CynaraBackend.h @@ -0,0 +1,50 @@ +#ifndef NETHER_CYNARA_BACKEND_H +#define NETHER_CYNARA_BACKEND_H + +// #ifdef HAVE_CYNARA + +#include +#include "nether_PolicyBackend.h" +#include + +#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 responseQueue; + int cynaraLastResult; +}; + +// #endif +#endif diff --git a/include/nether_DummyBackend.h b/include/nether_DummyBackend.h new file mode 100644 index 0000000..438e0f7 --- /dev/null +++ b/include/nether_DummyBackend.h @@ -0,0 +1,35 @@ +#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 diff --git a/include/nether_FileBackend.h b/include/nether_FileBackend.h new file mode 100644 index 0000000..4060153 --- /dev/null +++ b/include/nether_FileBackend.h @@ -0,0 +1,63 @@ +#ifndef NETHER_FILE_BACKEND_H +#define NETHER_FILE_BACKEND_H + +#include +#include +#include +#include +#include + +#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 split(const std::string &str, const std::string &delim); + private: + std::vector policy; +}; + +#endif diff --git a/include/nether_Manager.h b/include/nether_Manager.h new file mode 100644 index 0000000..8e58d56 --- /dev/null +++ b/include/nether_Manager.h @@ -0,0 +1,30 @@ +#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 diff --git a/include/nether_Netlink.h b/include/nether_Netlink.h new file mode 100644 index 0000000..8b934a7 --- /dev/null +++ b/include/nether_Netlink.h @@ -0,0 +1,33 @@ +#ifndef NETHER_NETLINK_H +#define NETHER_NETLINK_H + +#include "nether_Types.h" +#include "nether_Utils.h" + +class NetherManager; + +class NetherNetlink : public NetherPacketProcessor +{ + public: + NetherNetlink(NetherConfig &netherConfig); + ~NetherNetlink(); + const bool initialize(); + const bool reload(); + 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(); + const bool isValid(); + + protected: + NetherPacket *processedPacket; + + private: + struct nfq_q_handle *queueHandle; + struct nfq_handle *nfqHandle; + struct nlif_handle *nlif; + int fd; + uint32_t queue; +}; + +#endif // NETLINK_H_INCLUDED diff --git a/include/nether_PolicyBackend.h b/include/nether_PolicyBackend.h new file mode 100644 index 0000000..55879bf --- /dev/null +++ b/include/nether_PolicyBackend.h @@ -0,0 +1,24 @@ +#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 diff --git a/include/nether_Types.h b/include/nether_Types.h new file mode 100644 index 0000000..624abe8 --- /dev/null +++ b/include/nether_Types.h @@ -0,0 +1,192 @@ +#ifndef NETHER_TYPES_H +#define NETHER_TYPES_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 diff --git a/include/nether_Utils.h b/include/nether_Utils.h new file mode 100644 index 0000000..5d654e9 --- /dev/null +++ b/include/nether_Utils.h @@ -0,0 +1,164 @@ +#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 +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 +std::string stringFormat( const char* format, Args ... args ) +{ + size_t size = snprintf( nullptr, 0, format, args ... ) + 1; // Extra space for '\0' + std::unique_ptr 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 diff --git a/src/nether_CynaraBackend.cpp b/src/nether_CynaraBackend.cpp new file mode 100644 index 0000000..10b426e --- /dev/null +++ b/src/nether_CynaraBackend.cpp @@ -0,0 +1,156 @@ +#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(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(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 "<= 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 diff --git a/src/nether_FileBackend.cpp b/src/nether_FileBackend.cpp new file mode 100644 index 0000000..b73281e --- /dev/null +++ b/src/nether_FileBackend.cpp @@ -0,0 +1,101 @@ +#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 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"< NetherFileBackend::split(const std::string &str, const std::string &delim) +{ + std::vector 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); +} diff --git a/src/nether_Main.cpp b/src/nether_Main.cpp new file mode 100644 index 0000000..1ab5e9b --- /dev/null +++ b/src/nether_Main.cpp @@ -0,0 +1,167 @@ +#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=\t\t\tSet logging backend STDERR,SYSLOG,JOURNAL (default:"<< logBackendTypeToString(NETHER_LOG_BACKEND) << ")\n"; + cout<< " -L,--log-args=\t\tSet logging backend arguments\n"; + cout<< " -V,--verdict=\t\tWhat verdict to cast when policy backend is not available\n\t\t\t\t\tACCEPT,ALLOW_LOG,DENY (default:"<\t\tPrimary policy backend\n\t\t\t\t\tCYNARA,FILE,NONE (defualt:"<< backendTypeToString(NETHER_PRIMARY_BACKEND)<<")\n"; + cout<< " -P,--primary-backend-args=\tPrimary policy backend arguments\n"; + cout<< " -b,--backup-backend=\t\tBackup policy backend\n\t\t\t\t\tCYNARA,FILE,NONE (defualt:"<< backendTypeToString(NETHER_BACKUP_BACKEND)<< ")\n"; + cout<< " -B,--backup-backend-args=\tBackup policy backend arguments\n"; + cout<< " -q,--queue-num=\t\tNFQUEUE queue number to use for receiving packets\n"; + cout<< " -m,--mark-deny=\t\t\tPacket mark to use for DENY verdicts (default:"<< NETLINK_DROP_MARK << ")\n"; + cout<< " -M,--mark-allow-log=\t\tPacket mark to use for ALLOW_LOG verdicts (default:" << NETLINK_ALLOWLOG_MARK << ")\n"; + cout<< " -h,--help\t\t\t\tshow help information\n"; +} diff --git a/src/nether_Manager.cpp b/src/nether_Manager.cpp new file mode 100644 index 0000000..0ec0ae6 --- /dev/null +++ b/src/nether_Manager.cpp @@ -0,0 +1,253 @@ +#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; + fd_set watchedReadDescriptorsSet, watchedWriteDescriptorsSet; + struct timeval timeoutSpecification; + char packetBuffer[NETHER_PACKET_BUFFER_SIZE] __attribute__ ((aligned)); + + while (1) + { + FD_ZERO (&watchedReadDescriptorsSet); + FD_ZERO (&watchedWriteDescriptorsSet); + + /* Always listen for signals */ + FD_SET (signalDescriptor, &watchedReadDescriptorsSet); + + if ((netlinkDescriptor = netherNetlink->getDescriptor()) >= 0) + { + 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); + } + } + + timeoutSpecification.tv_sec = 240; + timeoutSpecification.tv_usec = 0; + + if (select (FD_SETSIZE, &watchedReadDescriptorsSet, &watchedWriteDescriptorsSet, NULL, &timeoutSpecification) < 0) + { + LOGE("select error " << strerror(errno)); + return (false); + } + + 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; + } + } + if (FD_ISSET(netlinkDescriptor, &watchedReadDescriptorsSet)) + { + LOGD("netlink descriptor active"); + + /* some data arrives on netlink, read it */ + if ((packetReadSize = recv(netlinkDescriptor, packetBuffer, sizeof(packetBuffer), 0)) >= 0) + { + /* try to process the packet using netfilter_queue library, fetch packet info + needed for making a decision about it */ + 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; + } + } + + if (packetReadSize < 0 && errno == ENOBUFS) + { + LOGI("NetherManager::process losing packets! [bad things might happen]"); + continue; + } + + LOGE("NetherManager::process recv failed " << strerror(errno)); + break; + } + 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); +} diff --git a/src/nether_Netlink.cpp b/src/nether_Netlink.cpp new file mode 100644 index 0000000..ed2dd67 --- /dev/null +++ b/src/nether_Netlink.cpp @@ -0,0 +1,150 @@ +#include "nether_Netlink.h" + +NetherNetlink::NetherNetlink(NetherConfig &netherConfig) + : nfqHandle(nullptr), queueHandle(nullptr), nlif(nullptr), + queue(netherConfig.queueNumber), + NetherPacketProcessor(netherConfig) +{ +} + +NetherNetlink::~NetherNetlink() +{ + if (queueHandle) nfq_destroy_queue(queueHandle); + if (nfqHandle) nfq_close(nfqHandle); +} + +const bool NetherNetlink::initialize() +{ + nfqHandle = nfq_open(); + + if (!nfqHandle) + { + LOGE("Error during nfq_open()"); + return (false); + } + + if (nfq_unbind_pf(nfqHandle, AF_INET) < 0) + { + LOGE("Error during nfq_unbind_pf() (no permission?)"); + return (false); + } + + if (nfq_bind_pf(nfqHandle, AF_INET) < 0) + { + LOGE("Error during nfq_bind_pf()"); + return (false); + } + + queueHandle = nfq_create_queue(nfqHandle, queue, &callback, this); + + if (!queueHandle) + { + LOGE("Error during nfq_create_queue()"); + return (false); + } + + if (nfq_set_queue_flags(queueHandle, NFQA_CFG_F_SECCTX, NFQA_CFG_F_SECCTX)) + LOGI("This kernel version does not allow to retrieve security context"); + + if (nfq_set_mode(queueHandle, NFQNL_COPY_PACKET, 0xffff) < 0) + { + LOGE("Can't set packet_copy mode"); + nfq_destroy_queue (queueHandle); + return (false); + } + + if (nfq_set_queue_flags(queueHandle, NFQA_CFG_F_UID_GID, NFQA_CFG_F_UID_GID)) + { + LOGE("This kernel version does not allow to retrieve process UID/GID"); + nfq_destroy_queue (queueHandle); + return (false); + } + + nlif = nlif_open(); + if (!nlif) + LOGI("Failed to initialize NLIF subsystem, interface information won't be available"); + + return (true); +} + +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); +} + +int NetherNetlink::callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data) +{ + NetherNetlink *me = static_cast(data); + NetherPacket packet; + unsigned char *secctx; + int secctxSize = 0; + struct nfqnl_msg_packet_hdr *ph; + unsigned char *payload; + + if ((ph = nfq_get_msg_packet_hdr(nfa))) + packet.id = ntohl(ph->packet_id); + else + { + LOGI("Failed to get packet id"); + return (1); + } + + if (!nfq_get_uid(nfa, &packet.uid)) + LOGW("Failed to get uid for packet id=" << packet.id); + + nfq_get_gid(nfa, &packet.gid); + + 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 */ + + return (0); +} + +const bool NetherNetlink::isValid() +{ + return (nfqHandle && queueHandle); +} + +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); +} diff --git a/src/nether_NetworkUtils.cpp b/src/nether_NetworkUtils.cpp new file mode 100644 index 0000000..c6e1072 --- /dev/null +++ b/src/nether_NetworkUtils.cpp @@ -0,0 +1,128 @@ +#include +#include +#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)"); + } +} -- 2.7.4