diff --git a/.gitignore b/.gitignore index 60400ec..0824e2e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ decpcap_test TAGS *.o *~ +nethogs.project + diff --git a/MakeLib.mk b/MakeLib.mk new file mode 100644 index 0000000..6a19b9a --- /dev/null +++ b/MakeLib.mk @@ -0,0 +1,94 @@ +VERSION := 0 +SUBVERSION := 8 +MINORVERSION := 2-SNAPSHOT + +#prefix := /usr +prefix := /usr/local +sbin := $(prefix)/lib + +all: libnethogs + +LDFLAGS:= -shared +CXXINCLUDES := +VISIBILITY=-fvisibility=hidden +ODIR_BASE := objs/lib + +ifeq ($(DEBUG),1) + # Debug mode options + $(info Bulding debug version) + ODIR:=$(ODIR_BASE)/debug + CFLAGS?=-Wall -Wextra -O0 -g -fPIC $(VISIBILITY) + CXXFLAGS?=--std=c++11 --std=c++11 -Wall -Wextra -O0 -g -fPIC $(VISIBILITY) $(CXXINCLUDES) +else + # Release mode options + ODIR:=$(ODIR_BASE)/release + CFLAGS?=-Wall -Wextra -O3 -fPIC $(VISIBILITY) + CXXFLAGS?=---std=c++11 Wall -Wextra -O3 -fPIC $(VISIBILITY) $(CXXINCLUDES) +endif + +OBJ_NAMES= libnethogs.o packet.o connection.o process.o refresh.o decpcap.o inode2prog.o conninode.o devices.o +OBJS=$(addprefix $(ODIR)/,$(OBJ_NAMES)) + +#$(info $(OBJS)) + +.PHONY: tgz + +.PHONY: uninstall + +install: libnethogs + install -d -m 755 $(DESTDIR)$(sbin) + install -m 755 libnethogs $(DESTDIR)$(sbin) + @echo + @echo "Installed libnethogs to $(DESTDIR)$(sbin)" + @echo + @echo "You might have to add this directory to your PATH and/or refresh your shells' path cache with a command like 'hash -r'." + +uninstall: + rm $(DESTDIR)$(sbin)/libnethogs + +libnethogs: $(OBJS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o libnethogs.so -lpcap + +#-lefence + +$(ODIR)/refresh.o: refresh.cpp refresh.h nethogs.h + @mkdir -p $(ODIR) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c refresh.cpp + +$(ODIR)/process.o: process.cpp process.h nethogs.h + @mkdir -p $(ODIR) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c process.cpp + +$(ODIR)/packet.o: packet.cpp packet.h nethogs.h + @mkdir -p $(ODIR) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c packet.cpp + +$(ODIR)/connection.o: connection.cpp connection.h nethogs.h + @mkdir -p $(ODIR) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c connection.cpp + +$(ODIR)/decpcap.o: decpcap.c decpcap.h + @mkdir -p $(ODIR) + $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c decpcap.c + +$(ODIR)/inode2prog.o: inode2prog.cpp inode2prog.h nethogs.h + @mkdir -p $(ODIR) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c inode2prog.cpp + +$(ODIR)/conninode.o: conninode.cpp nethogs.h conninode.h + @mkdir -p $(ODIR) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c conninode.cpp + +$(ODIR)/devices.o: devices.cpp devices.h + @mkdir -p $(ODIR) + $(CXX) $(CXXFLAGS) -o $@ -c devices.cpp + +$(ODIR)/libnethogs.o: libnethogs.cpp libnethogs.h + @mkdir -p $(ODIR) + $(CXX) $(CXXFLAGS) -o $@ -c libnethogs.cpp -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" + +.PHONY: clean +clean: + rm -f $(OBJS) + rm -f libnethogs.so + diff --git a/cui.cpp b/cui.cpp index df8fd06..f5576cb 100644 --- a/cui.cpp +++ b/cui.cpp @@ -32,7 +32,7 @@ #include #include "nethogs.h" #include "process.h" - +#include "refresh.h" std::string * caption; extern const char version[]; @@ -277,106 +277,6 @@ void ui_tick () } } -float tomb (u_int32_t bytes) -{ - return ((double)bytes) / 1024 / 1024; -} -float tokb (u_int32_t bytes) -{ - return ((double)bytes) / 1024; -} -float tokbps (u_int32_t bytes) -{ - return (((double)bytes) / PERIOD) / 1024; -} - -/** Get the kb/s values for this process */ -void getkbps (Process * curproc, float * recvd, float * sent) -{ - u_int32_t sum_sent = 0, - sum_recv = 0; - - /* walk though all this process's connections, and sum - * them up */ - ConnList * curconn = curproc->connections; - ConnList * previous = NULL; - while (curconn != NULL) - { - if (curconn->getVal()->getLastPacket() <= curtime.tv_sec - CONNTIMEOUT) - { - /* stalled connection, remove. */ - ConnList * todelete = curconn; - Connection * conn_todelete = curconn->getVal(); - curconn = curconn->getNext(); - if (todelete == curproc->connections) - curproc->connections = curconn; - if (previous != NULL) - previous->setNext(curconn); - delete (todelete); - delete (conn_todelete); - } - else - { - u_int32_t sent = 0, recv = 0; - curconn->getVal()->sumanddel(curtime, &recv, &sent); - sum_sent += sent; - sum_recv += recv; - previous = curconn; - curconn = curconn->getNext(); - } - } - *recvd = tokbps(sum_recv); - *sent = tokbps(sum_sent); -} - -/** get total values for this process */ -void gettotal(Process * curproc, u_int32_t * recvd, u_int32_t * sent) -{ - u_int32_t sum_sent = 0, - sum_recv = 0; - ConnList * curconn = curproc->connections; - while (curconn != NULL) - { - Connection * conn = curconn->getVal(); - sum_sent += conn->sumSent; - sum_recv += conn->sumRecv; - curconn = curconn->getNext(); - } - //std::cout << "Sum sent: " << sum_sent << std::endl; - //std::cout << "Sum recv: " << sum_recv << std::endl; - *recvd = sum_recv; - *sent = sum_sent; -} - -void gettotalmb(Process * curproc, float * recvd, float * sent) -{ - u_int32_t sum_sent = 0, - sum_recv = 0; - gettotal(curproc, &sum_recv, &sum_sent); - *recvd = tomb(sum_recv); - *sent = tomb(sum_sent); -} - -/** get total values for this process */ -void gettotalkb(Process * curproc, float * recvd, float * sent) -{ - u_int32_t sum_sent = 0, - sum_recv = 0; - gettotal(curproc, &sum_recv, &sum_sent); - *recvd = tokb(sum_recv); - *sent = tokb(sum_sent); -} - -void gettotalb(Process * curproc, float * recvd, float * sent) -{ - u_int32_t sum_sent = 0, - sum_recv = 0; - gettotal(curproc, &sum_recv, &sum_sent); - //std::cout << "Total sent: " << sum_sent << std::endl; - *sent = sum_sent; - *recvd = sum_recv; -} - void show_trace(Line * lines[], int nproc) { std::cout << "\nRefreshing:\n"; diff --git a/libnethogs.cpp b/libnethogs.cpp new file mode 100644 index 0000000..cbdba1b --- /dev/null +++ b/libnethogs.cpp @@ -0,0 +1,210 @@ +#include "libnethogs.h" +#include "nethogs.cpp" + +#include +#include +#include +#include +#include +#include + +////////////////////////////// +extern ProcList * processes; +extern Process * unknowntcp; +extern Process * unknownudp; +extern Process * unknownip; +////////////////////////////// + +static std::shared_ptr monitor_thread_ptr; +static std::atomic_bool monitor_thread_run_flag(false); +static NethogsMonitor::Callback monitor_udpate_callback; +static NethogsMonitorData monitor_data; + +bool NethogsMonitor::_trace = false; +bool NethogsMonitor::_promisc = false; + +void NethogsMonitor::threadProc() +{ + process_init(); + + device * devices = get_default_devices(); + if ( devices == NULL ) + { + std::cerr << "Not devices to monitor" << std::endl; + return; + } + + handle * handles = NULL; + device * current_dev = devices; + + while (current_dev != NULL) + { + if( !getLocal(current_dev->name, _trace) ) + { + std::cerr << "getifaddrs failed while establishing local IP." << std::endl; + continue; + } + + char errbuf[PCAP_ERRBUF_SIZE]; + dp_handle * newhandle = dp_open_live(current_dev->name, BUFSIZ, _promisc, 100, errbuf); + if (newhandle != NULL) + { + dp_addcb (newhandle, dp_packet_ip, process_ip); + dp_addcb (newhandle, dp_packet_ip6, process_ip6); + dp_addcb (newhandle, dp_packet_tcp, process_tcp); + dp_addcb (newhandle, dp_packet_udp, process_udp); + + /* The following code solves sf.net bug 1019381, but is only available + * in newer versions (from 0.8 it seems) of libpcap + * + * update: version 0.7.2, which is in debian stable now, should be ok + * also. + */ + if (dp_setnonblock (newhandle, 1, errbuf) == -1) + { + fprintf(stderr, "Error putting libpcap in nonblocking mode\n"); + } + handles = new handle (newhandle, current_dev->name, handles); + } + else + { + fprintf(stderr, "ERROR: opening handler for device %s: %s\n", + current_dev->name, strerror(errno)); + } + + current_dev = current_dev->next; + } + + signal (SIGALRM, &alarm_cb); + alarm (refreshdelay); + + fprintf(stderr, "Waiting for first packet to arrive (see sourceforge.net bug 1019381)\n"); + struct dpargs * userdata = (dpargs *) malloc (sizeof (struct dpargs)); + + time_t last_event_time = 0; + // Main loop: + // Walks though the 'handles' list, which contains handles opened in non-blocking mode. + // This causes the CPU utilisation to go up to 100%. This is tricky: + while (monitor_thread_run_flag) + { + bool packets_read = false; + + handle * current_handle = handles; + while (current_handle != NULL) + { + userdata->device = current_handle->devicename; + userdata->sa_family = AF_UNSPEC; + int retval = dp_dispatch (current_handle->content, -1, (u_char *)userdata, sizeof (struct dpargs)); + if (retval < 0) + { + std::cerr << "Error dispatching: " << retval << std::endl; + } + else if (retval != 0) + { + packets_read = true; + } + current_handle = current_handle->next; + } + + + if ( packets_read && needrefresh ) + { + needrefresh = false; + last_event_time = dp_get_lasttime(); + handleUpdate(); + } + + // If no packets were read at all this iteration, pause to prevent 100% + // CPU utilisation; + if (!packets_read) + { + usleep(100); + } + } +} + +void NethogsMonitor::handleUpdate() +{ + refreshconninode(); + refreshcount++; + + ProcList * curproc = processes; + ProcList * previousproc = NULL; + int nproc = processes->size(); + + while (curproc != NULL) + { + // walk though its connections, summing up their data, and + // throwing away connections that haven't received a package + // in the last PROCESSTIMEOUT seconds. + assert (curproc != NULL); + assert (curproc->getVal() != NULL); + assert (nproc == processes->size()); + + /* remove timed-out processes (unless it's one of the the unknown process) */ + if ((curproc->getVal()->getLastPacket() + PROCESSTIMEOUT <= curtime.tv_sec) + && (curproc->getVal() != unknowntcp) + && (curproc->getVal() != unknownudp) + && (curproc->getVal() != unknownip)) + { + if (DEBUG) + std::cout << "PROC: Deleting process\n"; + ProcList * todelete = curproc; + Process * p_todelete = curproc->getVal(); + if (previousproc) + { + previousproc->next = curproc->next; + curproc = curproc->next; + } else { + processes = curproc->getNext(); + curproc = processes; + } + delete todelete; + delete p_todelete; + nproc--; + //continue; + } + else + { + NethogsMonitorData::Line& line = monitor_data.apps_info[curproc->getVal()->name]; + if( line.app_name.empty() ) + { + line.app_name = curproc->getVal()->name; + } + if( line.device_name != curproc->getVal()->devicename ) + { + line.device_name = curproc->getVal()->devicename; + } + line.pid = curproc->getVal()->pid; + line.uid = curproc->getVal()->getUid(); + + getkbps (curproc->getVal(), &line.sent_kbs, &line.recv_kbs); + gettotal (curproc->getVal(), &line.recv_bytes, &line.sent_bytes); + + previousproc = curproc; + curproc = curproc->next; + } + } + + monitor_udpate_callback(monitor_data); +} + +void NethogsMonitor::start(NethogsMonitor::Callback const& cb) +{ + bool expected = false; + if( monitor_thread_run_flag.compare_exchange_strong(expected, true) ) + { + monitor_udpate_callback = cb; + monitor_thread_ptr = std::make_shared(&threadProc); + } +} + +void NethogsMonitor::stop() +{ + bool expected = true; + if( monitor_thread_run_flag.compare_exchange_strong(expected, false) ) + { + monitor_thread_ptr->join(); + monitor_udpate_callback = nullptr; + } +} diff --git a/libnethogs.h b/libnethogs.h new file mode 100644 index 0000000..7f16e3a --- /dev/null +++ b/libnethogs.h @@ -0,0 +1,48 @@ +#ifndef NETHOGSMINITOR_H +#define NETHOGSMINITOR_H + +#include +#include + +#define NETHOGS_DSO_VISIBLE __attribute__ ((visibility ("default"))) +#define NETHOGS_DSO_HIDDEN __attribute__ ((visibility ("hidden"))) + +class NETHOGS_DSO_VISIBLE NethogsMonitorData +{ + public: + class Line + { + public: + std::string app_name; + std::string device_name; + int uid; + int pid; + u_int32_t sent_bytes; + u_int32_t recv_bytes; + float sent_kbs; + float recv_kbs; + }; + std::map apps_info; +}; + +class NETHOGS_DSO_VISIBLE NethogsMonitor +{ + NethogsMonitor(); +public: + typedef std::function Callback; + + static void start(Callback const& cb); + static void stop(); + +private: + static void threadProc(); + static void handleUpdate(); + + static bool _trace; + static bool _promisc; +}; + +#undef NETHOGS_DSO_VISIBLE +#undef NETHOGS_DSO_HIDDEN + +#endif // NETHOGSMINITOR_H diff --git a/libnethogs.so b/libnethogs.so new file mode 100755 index 0000000..1353d2b Binary files /dev/null and b/libnethogs.so differ diff --git a/main.cpp b/main.cpp index 2a417d5..f09fb57 100644 --- a/main.cpp +++ b/main.cpp @@ -30,6 +30,32 @@ static void help(bool iserror) output << " m: switch between total (KB, B, MB) and KB/s mode\n"; } +void quit_cb (int /* i */) +{ + procclean(); + if ((!tracemode) && (!DEBUG)) + exit_ui(); + exit(0); +} + +void forceExit(bool success, const char *msg, ...) +{ + if ((!tracemode)&&(!DEBUG)){ + exit_ui(); + } + + va_list argp; + va_start(argp, msg); + vfprintf(stderr, msg, argp); + va_end(argp); + std::cerr << std::endl; + + if (success) + exit(EXIT_SUCCESS); + else + exit(EXIT_FAILURE); +} + int main (int argc, char** argv) { process_init(); @@ -111,7 +137,11 @@ int main (int argc, char** argv) handle * handles = NULL; device * current_dev = devices; while (current_dev != NULL) { - getLocal(current_dev->name, tracemode); + + if( !getLocal(current_dev->name, tracemode) ) + { + forceExit(false, "getifaddrs failed while establishing local IP."); + } dp_handle * newhandle = dp_open_live(current_dev->name, BUFSIZ, promisc, 100, errbuf); if (newhandle != NULL) diff --git a/nethogs.cpp b/nethogs.cpp index 3f20df9..3c30659 100644 --- a/nethogs.cpp +++ b/nethogs.cpp @@ -227,32 +227,6 @@ int process_ip6 (u_char * userdata, const dp_header * /* header */, const u_char return false; } -void quit_cb (int /* i */) -{ - procclean(); - if ((!tracemode) && (!DEBUG)) - exit_ui(); - exit(0); -} - -void forceExit(bool success, const char *msg, ...) -{ - if ((!tracemode)&&(!DEBUG)){ - exit_ui(); - } - - va_list argp; - va_start(argp, msg); - vfprintf(stderr, msg, argp); - va_end(argp); - std::cerr << std::endl; - - if (success) - exit(EXIT_SUCCESS); - else - exit(EXIT_FAILURE); -} - class handle { public: handle (dp_handle * m_handle, const char * m_devicename = NULL, diff --git a/packet.cpp b/packet.cpp index fc69a9c..7e37aad 100644 --- a/packet.cpp +++ b/packet.cpp @@ -48,11 +48,11 @@ local_addr * local_addrs = NULL; * uses getifaddrs to get addresses of this device, and adds them to the * local_addrs-list. */ -void getLocal (const char *device, bool tracemode) +bool getLocal (const char *device, bool tracemode) { struct ifaddrs *ifaddr, *ifa; if(getifaddrs(&ifaddr) == -1) { - forceExit(false, "getifaddrs failed while establishing local IP."); + return false; } for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { @@ -81,6 +81,7 @@ void getLocal (const char *device, bool tracemode) } } } + return true; } typedef u_int32_t tcp_seq; diff --git a/packet.h b/packet.h index 7b28eec..dc497d7 100644 --- a/packet.h +++ b/packet.h @@ -39,7 +39,7 @@ enum direction { /* To initialise this module, call getLocal with the currently * monitored device (e.g. "eth0:1") */ -void getLocal (const char *device, bool tracemode); +bool getLocal (const char *device, bool tracemode); class Packet { diff --git a/refresh.cpp b/refresh.cpp index 5195983..05136b9 100644 --- a/refresh.cpp +++ b/refresh.cpp @@ -23,10 +23,11 @@ #include #include #include -#include "nethogs.h" +#include "process.h" extern bool needrefresh; extern unsigned refreshdelay; +extern timeval curtime; void alarm_cb (int /*i*/) { @@ -37,3 +38,104 @@ void alarm_cb (int /*i*/) alarm(refreshdelay); } + +float tomb (u_int32_t bytes) +{ + return ((double)bytes) / 1024 / 1024; +} +float tokb (u_int32_t bytes) +{ + return ((double)bytes) / 1024; +} + +float tokbps (u_int32_t bytes) +{ + return (((double)bytes) / PERIOD) / 1024; +} + +/** Get the kb/s values for this process */ +void getkbps (Process * curproc, float * recvd, float * sent) +{ + u_int32_t sum_sent = 0, + sum_recv = 0; + + /* walk though all this process's connections, and sum + * them up */ + ConnList * curconn = curproc->connections; + ConnList * previous = NULL; + while (curconn != NULL) + { + if (curconn->getVal()->getLastPacket() <= curtime.tv_sec - CONNTIMEOUT) + { + /* stalled connection, remove. */ + ConnList * todelete = curconn; + Connection * conn_todelete = curconn->getVal(); + curconn = curconn->getNext(); + if (todelete == curproc->connections) + curproc->connections = curconn; + if (previous != NULL) + previous->setNext(curconn); + delete (todelete); + delete (conn_todelete); + } + else + { + u_int32_t sent = 0, recv = 0; + curconn->getVal()->sumanddel(curtime, &recv, &sent); + sum_sent += sent; + sum_recv += recv; + previous = curconn; + curconn = curconn->getNext(); + } + } + *recvd = tokbps(sum_recv); + *sent = tokbps(sum_sent); +} + +/** get total values for this process */ +void gettotal(Process * curproc, u_int32_t * recvd, u_int32_t * sent) +{ + u_int32_t sum_sent = 0, + sum_recv = 0; + ConnList * curconn = curproc->connections; + while (curconn != NULL) + { + Connection * conn = curconn->getVal(); + sum_sent += conn->sumSent; + sum_recv += conn->sumRecv; + curconn = curconn->getNext(); + } + //std::cout << "Sum sent: " << sum_sent << std::endl; + //std::cout << "Sum recv: " << sum_recv << std::endl; + *recvd = sum_recv; + *sent = sum_sent; +} + +void gettotalmb(Process * curproc, float * recvd, float * sent) +{ + u_int32_t sum_sent = 0, + sum_recv = 0; + gettotal(curproc, &sum_recv, &sum_sent); + *recvd = tomb(sum_recv); + *sent = tomb(sum_sent); +} + +/** get total values for this process */ +void gettotalkb(Process * curproc, float * recvd, float * sent) +{ + u_int32_t sum_sent = 0, + sum_recv = 0; + gettotal(curproc, &sum_recv, &sum_sent); + *recvd = tokb(sum_recv); + *sent = tokb(sum_sent); +} + +void gettotalb(Process * curproc, float * recvd, float * sent) +{ + u_int32_t sum_sent = 0, + sum_recv = 0; + gettotal(curproc, &sum_recv, &sum_sent); + //std::cout << "Total sent: " << sum_sent << std::endl; + *sent = sum_sent; + *recvd = sum_recv; +} diff --git a/refresh.h b/refresh.h index 152907f..a1cf6cc 100644 --- a/refresh.h +++ b/refresh.h @@ -19,5 +19,13 @@ * */ - void alarm_cb (int i); + +float tomb (u_int32_t bytes); +float tokb (u_int32_t bytes); +float tokbps (u_int32_t bytes); +void getkbps (Process * curproc, float * recvd, float * sent); +void gettotal(Process * curproc, u_int32_t * recvd, u_int32_t * sent); +void gettotalmb(Process * curproc, float * recvd, float * sent); +void gettotalkb(Process * curproc, float * recvd, float * sent); +void gettotalb(Process * curproc, float * recvd, float * sent);