nethogs/Changelog0000644000175000017500000000416511437270460014621 0ustar arnoutenarnoutenChangelog 31/08/10 (Arnout) - support for screens wider than 80 characters, thanks to Shock at https://bugs.launchpad.net/ubuntu/+source/nethogs/+bug/627626 10/08/08 (Arnout) - compile with g++ 4.3, thanks to Leslie P. Polzer - skypher 08/06/08 (Arnout, thanks to MeneerJansen for sparking some activity again :) ) - add 'if (DEBUG)' to debug message - fix compiler warning by marking some constant strings 'const' - set view mode to 'kbps' by default, instead of the 'grand total' number of bytes - some fixes thanks to Bart Martens 27/08/05 (Arnout) - giving all unknown connections their own `unknown' process - UDP support - investigated memleak, turns out to be a problem in libc: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=273051 https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=103142 18/09/04 (Arnout) - also compile with gcc-3.4 ------------------------------------------------- 17/09/04 (Arnout) -Wrapped pcap stuff in nicer 'decpcap' module -Support for IPv6 and PPP -using standard C++ maps instead of custom structures -many more code cleanups -documentation -split up ui code in seperate module ------------------------------------------------- 15/08/04 (Fabian) -Totals (in curses mode) ------------------------------------------------- 29/07/04 (Fabian) -tracemode (-t) ------------------------------------------------- 06/07/04 (Arnout) -added support for monitoring multiple interfaces at once ------------------------------------------------- 02/07/04 (Arnout) -enabled 'q' for quitting ------------------------------------------------- 29/06/04 (Fabian) <-> 0.5.1 -Adding forceExit when device is ifdown (handle was null => segfault) -Changelog activated ------------------------------------------------- 10/05/04 (Arnout) -cleanups -splitting out incoming and outgoing traffic (based on 'I/O by balance' by Fabian) ------------------------------------------------- 06/04/04 (Fabian) -getLocal -I/O by balance -forceExit ------------------------------------------------- 14/02/04 (Fabian) -Refresh delay -Help -Handling command-line options ------------------------------------------------- nethogs/connection.cpp0000644000175000017500000001043311126712472015644 0ustar arnoutenarnouten#include #include #include #include "nethogs.h" #include "connection.h" class ConnList { public: ConnList (Connection * m_val = NULL, ConnList * m_next = NULL) { val = m_val; next = m_next; } Connection * val; ConnList * next; }; ConnList * connections = NULL; void PackList::add (Packet * p) { if (content == NULL) { content = new PackListNode (new Packet (*p)); return; } if (content->val->time.tv_sec == p->time.tv_sec) { content->val->len += p->len; return; } /* store copy of packet, so that original may be freed */ content = new PackListNode(new Packet (*p), content); } /* sums up the total bytes used and removes 'old' packets */ u_int32_t PackList::sumanddel (timeval t) { u_int32_t retval = 0; PackListNode * current = content; PackListNode * previous = NULL; while (current != NULL) { //std::cout << "Comparing " << current->val->time.tv_sec << " <= " << t.tv_sec - PERIOD << endl; if (current->val->time.tv_sec <= t.tv_sec - PERIOD) { if (current == content) content = NULL; else if (previous != NULL) previous->next = NULL; delete current; return retval; } retval += current->val->len; previous = current; current = current->next; } return retval; } /* packet may be deleted by caller */ Connection::Connection (Packet * packet) { assert (packet != NULL); connections = new ConnList (this, connections); sent_packets = new PackList (); recv_packets = new PackList (); sumSent = 0; sumRecv = 0; if (DEBUG) { std::cout << "New connection, with package len " << packet->len << std::endl; } if (packet->Outgoing()) { sumSent += packet->len; sent_packets->add(packet); refpacket = new Packet (*packet); } else { sumRecv += packet->len; recv_packets->add(packet); refpacket = packet->newInverted(); } lastpacket = packet->time.tv_sec; if (DEBUG) std::cout << "New reference packet created at " << refpacket << std::endl; } Connection::~Connection () { if (DEBUG) std::cout << "Deleting connection" << std::endl; /* refpacket is not a pointer to one of the packets in the lists * so deleted */ delete (refpacket); if (sent_packets != NULL) delete sent_packets; if (recv_packets != NULL) delete recv_packets; ConnList * curr_conn = connections; ConnList * prev_conn = NULL; while (curr_conn != NULL) { if (curr_conn->val == this) { ConnList * todelete = curr_conn; curr_conn = curr_conn->next; if (prev_conn == NULL) { connections = curr_conn; } else { prev_conn->next = curr_conn; } delete (todelete); } else { prev_conn = curr_conn; curr_conn = curr_conn->next; } } } /* the packet will be freed by the calling code */ void Connection::add (Packet * packet) { lastpacket = packet->time.tv_sec; if (packet->Outgoing()) { if (DEBUG) { std::cout << "Outgoing: " << packet->len << std::endl; } sumSent += packet->len; sent_packets->add (packet); } else { if (DEBUG) { std::cout << "Incoming: " << packet->len << std::endl; } sumRecv += packet->len; if (DEBUG) { std::cout << "sumRecv now: " << sumRecv << std::endl; } recv_packets->add (packet); } } /* * finds connection to which this packet belongs. * a packet belongs to a connection if it matches * to its reference packet */ Connection * findConnection (Packet * packet) { ConnList * current = connections; while (current != NULL) { /* the reference packet is always *outgoing* */ if (packet->match(current->val->refpacket)) { return current->val; } current = current->next; } // Try again, now with the packet inverted: current = connections; Packet * invertedPacket = packet->newInverted(); while (current != NULL) { /* the reference packet is always *outgoing* */ if (invertedPacket->match(current->val->refpacket)) { delete invertedPacket; return current->val; } current = current->next; } delete invertedPacket; return NULL; } /* * Connection::sumanddel * * sums up the total bytes used * and removes 'old' packets. * * Returns sum of sent packages (by address) * sum of recieved packages (by address) */ void Connection::sumanddel (timeval t, u_int32_t * recv, u_int32_t * sent) { (*sent)=(*recv)=0; *sent = sent_packets->sumanddel(t); *recv = recv_packets->sumanddel(t); } nethogs/connection.h0000644000175000017500000000340711126712472015314 0ustar arnoutenarnouten#ifndef __CONNECTION_H #define __CONNECTION_H #include #include "packet.h" class PackListNode { public: PackListNode (Packet * m_val, PackListNode * m_next = NULL) { val = m_val; next = m_next; } ~PackListNode () { delete val; if (next != NULL) delete next; } PackListNode * next; Packet * val; }; class PackList { public: PackList () { content = NULL; } PackList (Packet * m_val) { assert (m_val != NULL); content = new PackListNode(m_val); } ~PackList () { if (content != NULL) delete content; } /* sums up the total bytes used and removes 'old' packets */ u_int32_t sumanddel (timeval t); /* calling code may delete packet */ void add (Packet * p); private: PackListNode * content; }; class Connection { public: /* constructs a connection, makes a copy of * the packet as 'refpacket', and adds the * packet to the packlist */ /* packet may be deleted by caller */ Connection (Packet * packet); ~Connection(); /* add a packet to the packlist * will delete the packet structure * when it is 'merged with' (added to) another * packet */ void add (Packet * packet); int getLastPacket () { return lastpacket; } /* sums up the total bytes used * and removes 'old' packets. */ void sumanddel(timeval curtime, u_int32_t * recv, u_int32_t * sent); /* for checking if a packet is part of this connection */ /* the reference packet is always *outgoing*. */ Packet * refpacket; /* total sum or sent/received bytes */ u_int32_t sumSent; u_int32_t sumRecv; private: PackList * sent_packets; PackList * recv_packets; int lastpacket; }; /* Find the connection this packet belongs to */ /* (the calling code may free the packet afterwards) */ Connection * findConnection (Packet * packet); #endif nethogs/conninode.cpp0000644000175000017500000001206711176256660015475 0ustar arnoutenarnouten#include #include #include #include #include "nethogs.h" #include "conninode.h" extern local_addr * local_addrs; /* * connection-inode table. takes information from /proc/net/tcp. * key contains source ip, source port, destination ip, destination * port in format: '1.2.3.4:5-1.2.3.4:5' */ std::map conninode; /* * parses a /proc/net/tcp-line of the form: * sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode * 10: 020310AC:1770 9DD8A9C3:A525 01 00000000:00000000 00:00000000 00000000 0 0 2119 1 c0f4f0c0 206 40 10 3 -1 * 11: 020310AC:0404 936B2ECF:0747 01 00000000:00000000 00:00000000 00000000 1000 0 2109 1 c0f4fc00 368 40 20 2 -1 * * and of the form: * 2: 0000000000000000FFFF0000020310AC:0016 0000000000000000FFFF00009DD8A9C3:A526 01 00000000:00000000 02:000A7214 00000000 0 0 2525 2 c732eca0 201 40 1 2 -1 * */ void addtoconninode (char * buffer) { short int sa_family; struct in6_addr result_addr_local; struct in6_addr result_addr_remote; char rem_addr[128], local_addr[128]; int local_port, rem_port; struct in6_addr in6_local; struct in6_addr in6_remote; // this leaked memory //unsigned long * inode = (unsigned long *) malloc (sizeof(unsigned long)); unsigned long inode; int matches = sscanf(buffer, "%*d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %*X %*X:%*X %*X:%*X %*X %*d %*d %ld %*512s\n", local_addr, &local_port, rem_addr, &rem_port, &inode); if (matches != 5) { fprintf(stderr,"Unexpected buffer: '%s'\n",buffer); exit(0); } if (inode == 0) { /* connection is in TIME_WAIT state. We rely on * the old data still in the table. */ return; } if (strlen(local_addr) > 8) { /* this is an IPv6-style row */ /* Demangle what the kernel gives us */ sscanf(local_addr, "%08X%08X%08X%08X", &in6_local.s6_addr32[0], &in6_local.s6_addr32[1], &in6_local.s6_addr32[2], &in6_local.s6_addr32[3]); sscanf(rem_addr, "%08X%08X%08X%08X", &in6_remote.s6_addr32[0], &in6_remote.s6_addr32[1], &in6_remote.s6_addr32[2], &in6_remote.s6_addr32[3]); if ((in6_local.s6_addr32[0] == 0x0) && (in6_local.s6_addr32[1] == 0x0) && (in6_local.s6_addr32[2] == 0xFFFF0000)) { /* IPv4-compatible address */ result_addr_local = *((struct in6_addr*) &(in6_local.s6_addr32[3])); result_addr_remote = *((struct in6_addr*) &(in6_remote.s6_addr32[3])); sa_family = AF_INET; } else { /* real IPv6 address */ //inet_ntop(AF_INET6, &in6_local, addr6, sizeof(addr6)); //INET6_getsock(addr6, (struct sockaddr *) &localaddr); //inet_ntop(AF_INET6, &in6_remote, addr6, sizeof(addr6)); //INET6_getsock(addr6, (struct sockaddr *) &remaddr); //localaddr.sin6_family = AF_INET6; //remaddr.sin6_family = AF_INET6; result_addr_local = in6_local; result_addr_remote = in6_remote; sa_family = AF_INET6; } } else { /* this is an IPv4-style row */ sscanf(local_addr, "%X", (unsigned int *) &result_addr_local); sscanf(rem_addr, "%X", (unsigned int *) &result_addr_remote); sa_family = AF_INET; } char * hashkey = (char *) malloc (HASHKEYSIZE * sizeof(char)); char * local_string = (char*) malloc (50); char * remote_string = (char*) malloc (50); inet_ntop(sa_family, &result_addr_local, local_string, 49); inet_ntop(sa_family, &result_addr_remote, remote_string, 49); snprintf(hashkey, HASHKEYSIZE * sizeof(char), "%s:%d-%s:%d", local_string, local_port, remote_string, rem_port); free (local_string); //if (DEBUG) // fprintf (stderr, "Hashkey: %s\n", hashkey); //std::cout << "Adding to conninode\n" << std::endl; conninode[hashkey] = inode; /* workaround: sometimes, when a connection is actually from 172.16.3.1 to * 172.16.3.3, packages arrive from 195.169.216.157 to 172.16.3.3, where * 172.16.3.1 and 195.169.216.157 are the local addresses of different * interfaces */ struct local_addr * current_local_addr = local_addrs; while (current_local_addr != NULL) { /* TODO maybe only add the ones with the same sa_family */ snprintf(hashkey, HASHKEYSIZE * sizeof(char), "%s:%d-%s:%d", current_local_addr->string, local_port, remote_string, rem_port); conninode[hashkey] = inode; current_local_addr = current_local_addr->next; } free (hashkey); free (remote_string); } /* opens /proc/net/tcp[6] and adds its contents line by line */ int addprocinfo (const char * filename) { FILE * procinfo = fopen (filename, "r"); char buffer[8192]; if (procinfo == NULL) return 0; fgets(buffer, sizeof(buffer), procinfo); do { if (fgets(buffer, sizeof(buffer), procinfo)) addtoconninode(buffer); } while (!feof(procinfo)); fclose(procinfo); return 1; } void refreshconninode () { /* we don't forget old mappings, just overwrite */ //delete conninode; //conninode = new HashTable (256); if (! addprocinfo ("/proc/net/tcp")) { std::cout << "Error: couldn't open /proc/net/tcp\n"; exit(0); } addprocinfo ("/proc/net/tcp6"); //if (DEBUG) // reviewUnknown(); } nethogs/conninode.h0000644000175000017500000000010411030252761015112 0ustar arnoutenarnouten// handling the connection->inode mapping void refreshconninode (); nethogs/cui.cpp0000644000175000017500000002555511626170102014271 0ustar arnoutenarnouten/* NetHogs console UI */ #include #include #include #include #include #include #include #include "nethogs.h" #include "process.h" std::string * caption; //extern char [] version; const char version[] = " version " VERSION "." SUBVERSION "." MINORVERSION; extern ProcList * processes; extern timeval curtime; extern Process * unknowntcp; extern Process * unknownudp; extern Process * unknownip; // sort on sent or received? bool sortRecv = true; // viewMode: kb/s or total int VIEWMODE_KBPS = 0; int VIEWMODE_TOTAL_KB = 1; int VIEWMODE_TOTAL_B = 2; int VIEWMODE_TOTAL_MB = 3; int viewMode = VIEWMODE_KBPS; int nViewModes = 4; class Line { public: Line (const char * name, double n_recv_value, double n_sent_value, pid_t pid, uid_t uid, const char * n_devicename) { assert (pid >= 0); m_name = name; sent_value = n_sent_value; recv_value = n_recv_value; devicename = n_devicename; m_pid = pid; m_uid = uid; assert (m_pid >= 0); } void show (int row, unsigned int proglen); double sent_value; double recv_value; private: const char * m_name; const char * devicename; pid_t m_pid; uid_t m_uid; }; char * uid2username (uid_t uid) { struct passwd * pwd = NULL; /* getpwuid() allocates space for this itself, * which we shouldn't free */ pwd = getpwuid(uid); if (pwd == NULL) { assert(false); return strdup ("unlisted"); } else { return strdup(pwd->pw_name); } } void Line::show (int row, unsigned int proglen) { assert (m_pid >= 0); assert (m_pid <= 100000); if (DEBUG || tracemode) { std::cout << m_name << '/' << m_pid << '/' << m_uid << "\t" << sent_value << "\t" << recv_value << std::endl; return; } if (m_pid == 0) mvprintw (3+row, 0, "?"); else mvprintw (3+row, 0, "%d", m_pid); char * username = uid2username(m_uid); mvprintw (3+row, 6, "%s", username); free (username); if (strlen (m_name) > proglen) { // truncate oversized names char * tmp = strdup(m_name); char * start = tmp + strlen (m_name) - proglen; start[0] = '.'; start[1] = '.'; mvprintw (3+row, 6 + 9, "%s", start); free (tmp); } else { mvprintw (3+row, 6 + 9, "%s", m_name); } mvprintw (3+row, 6 + 9 + proglen + 2, "%s", devicename); mvprintw (3+row, 6 + 9 + proglen + 2 + 6, "%10.3f", sent_value); mvprintw (3+row, 6 + 9 + proglen + 2 + 6 + 9 + 3, "%10.3f", recv_value); if (viewMode == VIEWMODE_KBPS) { mvprintw (3+row, 6 + 9 + proglen + 2 + 6 + 9 + 3 + 11, "KB/sec"); } else if (viewMode == VIEWMODE_TOTAL_MB) { mvprintw (3+row, 6 + 9 + proglen + 2 + 6 + 9 + 3 + 11, "MB "); } else if (viewMode == VIEWMODE_TOTAL_KB) { mvprintw (3+row, 6 + 9 + proglen + 2 + 6 + 9 + 3 + 11, "KB "); } else if (viewMode == VIEWMODE_TOTAL_B) { mvprintw (3+row, 6 + 9 + proglen + 2 + 6 + 9 + 3 + 11, "B "); } } int GreatestFirst (const void * ma, const void * mb) { Line ** pa = (Line **)ma; Line ** pb = (Line **)mb; Line * a = *pa; Line * b = *pb; double aValue; if (sortRecv) { aValue = a->recv_value; } else { aValue = a->sent_value; } double bValue; if (sortRecv) { bValue = b->recv_value; } else { bValue = b->sent_value; } if (aValue > bValue) { return -1; } if (aValue == bValue) { return 0; } return 1; } void init_ui () { WINDOW * screen = initscr(); raw(); noecho(); cbreak(); nodelay(screen, TRUE); caption = new std::string ("NetHogs"); caption->append(version); //caption->append(", running at "); } void exit_ui () { clear(); endwin(); delete caption; } void ui_tick () { switch (getch()) { case 'q': /* quit */ quit_cb(0); break; case 's': /* sort on 'sent' */ sortRecv = false; break; case 'r': /* sort on 'received' */ sortRecv = true; break; case 'm': /* switch mode: total vs kb/s */ viewMode = (viewMode + 1) % nViewModes; break; } } 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; } // Display all processes and relevant network traffic using show function void do_refresh() { int row; // number of terminal rows int col; // number of terminal columns unsigned int proglen; // max length of the "PROGRAM" column getmaxyx(stdscr, row, col); /* find the boundaries of the screeen */ if (col < 60) { clear(); mvprintw(0,0, "The terminal is too narrow! Please make it wider.\nI'll wait..."); return; } if (col > PROGNAME_WIDTH) col = PROGNAME_WIDTH; proglen = col - 53; refreshconninode(); if (DEBUG || tracemode) { std::cout << "\nRefreshing:\n"; } else { clear(); mvprintw (0, 0, "%s", caption->c_str()); attron(A_REVERSE); mvprintw (2, 0, " PID USER %-*.*s DEV SENT RECEIVED ", proglen, proglen, "PROGRAM"); attroff(A_REVERSE); } ProcList * curproc = processes; ProcList * previousproc = NULL; int nproc = processes->size(); /* initialise to null pointers */ Line * lines [nproc]; int n = 0, i = 0; double sent_global = 0; double recv_global = 0; #ifndef NDEBUG // initialise to null pointers for (int i = 0; i < nproc; i++) lines[i] = NULL; #endif 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 { // add a non-timed-out process to the list of stuff to show float value_sent = 0, value_recv = 0; if (viewMode == VIEWMODE_KBPS) { //std::cout << "kbps viemode" << std::endl; getkbps (curproc->getVal(), &value_recv, &value_sent); } else if (viewMode == VIEWMODE_TOTAL_KB) { //std::cout << "total viemode" << std::endl; gettotalkb(curproc->getVal(), &value_recv, &value_sent); } else if (viewMode == VIEWMODE_TOTAL_MB) { //std::cout << "total viemode" << std::endl; gettotalmb(curproc->getVal(), &value_recv, &value_sent); } else if (viewMode == VIEWMODE_TOTAL_B) { //std::cout << "total viemode" << std::endl; gettotalb(curproc->getVal(), &value_recv, &value_sent); } else { forceExit("Invalid viewmode"); } uid_t uid = curproc->getVal()->getUid(); #ifndef NDEBUG struct passwd * pwuid = getpwuid(uid); assert (pwuid != NULL); // value returned by pwuid should not be freed, according to // Petr Uzel. //free (pwuid); #endif assert (curproc->getVal()->pid >= 0); assert (n < nproc); lines[n] = new Line (curproc->getVal()->name, value_recv, value_sent, curproc->getVal()->pid, uid, curproc->getVal()->devicename); previousproc = curproc; curproc = curproc->next; n++; #ifndef NDEBUG assert (nproc == processes->size()); if (curproc == NULL) assert (n-1 < nproc); else assert (n < nproc); #endif } } /* sort the accumulated lines */ qsort (lines, nproc, sizeof(Line *), GreatestFirst); /* print them */ for (i=0; ishow(i, proglen); recv_global += lines[i]->recv_value; sent_global += lines[i]->sent_value; delete lines[i]; } if (tracemode || DEBUG) { /* print the 'unknown' connections, for debugging */ ConnList * curr_unknownconn = unknowntcp->connections; while (curr_unknownconn != NULL) { std::cout << "Unknown connection: " << curr_unknownconn->getVal()->refpacket->gethashstring() << std::endl; curr_unknownconn = curr_unknownconn->getNext(); } } if ((!tracemode) && (!DEBUG)){ attron(A_REVERSE); mvprintw (3+1+i, 0, " TOTAL %-*.*s %10.3f %10.3f ", proglen, proglen, " ", sent_global, recv_global); if (viewMode == VIEWMODE_KBPS) { mvprintw (3+1+i, col - 7, "KB/sec "); } else if (viewMode == VIEWMODE_TOTAL_B) { mvprintw (3+1+i, col - 7, "B "); } else if (viewMode == VIEWMODE_TOTAL_KB) { mvprintw (3+1+i, col - 7, "KB "); } else if (viewMode == VIEWMODE_TOTAL_MB) { mvprintw (3+1+i, col - 7, "MB "); } attroff(A_REVERSE); mvprintw (4+1+i, 0, ""); refresh(); } } nethogs/cui.h0000644000175000017500000000022310122634650013722 0ustar arnoutenarnouten/* NetHogs console UI */ void do_refresh (); void init_ui (); void exit_ui (); /* periodically gives some CPU-time to the UI */ void ui_tick (); nethogs/decpcap.c0000644000175000017500000001674311607136467014566 0ustar arnoutenarnouten#include #include #include #include #include #include // for memcpy #include #include "decpcap.h" #define DP_DEBUG 0 /* functions to set up a handle (which is basically just a pcap handle) */ struct dp_handle * dp_fillhandle(pcap_t * phandle) { struct dp_handle * retval = (struct dp_handle *) malloc (sizeof (struct dp_handle)); int i; retval->pcap_handle = phandle; for (i = 0; i < dp_n_packet_types; i++) { retval->callback[i] = NULL; } retval->linktype = pcap_datalink(retval->pcap_handle); switch (retval->linktype) { case (DLT_EN10MB): fprintf(stdout, "Ethernet link detected\n"); break; case (DLT_PPP): fprintf(stdout, "PPP link detected\n"); break; case (DLT_LINUX_SLL): fprintf(stdout, "Linux Cooked Socket link detected\n"); break; default: fprintf(stdout, "No PPP or Ethernet link: %d\n", retval->linktype); // TODO maybe error? or 'other' callback? break; } return retval; } struct dp_handle * dp_open_offline(char * fname, char * ebuf) { pcap_t * temp = pcap_open_offline(fname, ebuf); if (temp == NULL) { return NULL; } return dp_fillhandle(temp); } struct dp_handle * dp_open_live(const char * device, int snaplen, int promisc, int to_ms, const char * ebuf) { pcap_t * temp = pcap_open_live(device, snaplen, promisc, to_ms, ebuf); if (temp == NULL) { return NULL; } return dp_fillhandle(temp); } /* functions to add callbacks */ void dp_addcb (struct dp_handle * handle, enum dp_packet_type type, dp_callback callback) { handle->callback[type] = callback; } /* functions for parsing the payloads */ void dp_parse_tcp (struct dp_handle * handle, const dp_header * header, const u_char * packet) { //const struct tcphdr * tcp = (struct tcphdr *) packet; //u_char * payload = (u_char *) packet + sizeof (struct tcphdr); if (handle->callback[dp_packet_tcp] != NULL) { int done = (handle->callback[dp_packet_tcp]) (handle->userdata, header, packet); if (done) return; } // TODO: maybe `pass on' payload to lower-level protocol parsing } void dp_parse_ip (struct dp_handle * handle, const dp_header * header, const u_char * packet) { const struct ip * ip = (struct ip *) packet; if (DP_DEBUG) { fprintf(stdout, "Looking at packet with length %ud\n", header->len); } u_char * payload = (u_char *) packet + sizeof (struct ip); if (handle->callback[dp_packet_ip] != NULL) { int done = (handle->callback[dp_packet_ip]) (handle->userdata, header, packet); if (done) return; } switch (ip->ip_p) { case (6): dp_parse_tcp (handle, header, payload); break; default: // TODO: maybe support for non-tcp IP packets break; } } void dp_parse_ip6 (struct dp_handle * handle, const dp_header * header, const u_char * packet) { const struct ip6_hdr * ip6 = (struct ip6_hdr *) packet; u_char * payload = (u_char *) packet + sizeof (struct ip6_hdr); if (handle->callback[dp_packet_ip6] != NULL) { int done = (handle->callback[dp_packet_ip6]) (handle->userdata, header, packet); if (done) return; } switch ((ip6->ip6_ctlun).ip6_un1.ip6_un1_nxt) { case (6): dp_parse_tcp (handle, header, payload); break; default: // TODO: maybe support for non-tcp ipv6 packets break; } } void dp_parse_ethernet (struct dp_handle * handle, const dp_header * header, const u_char * packet) { const struct ether_header * ethernet = (struct ether_header *)packet; u_char * payload = (u_char *) packet + sizeof (struct ether_header); /* call handle if it exists */ if (handle->callback[dp_packet_ethernet] != NULL) { int done = (handle->callback[dp_packet_ethernet]) (handle->userdata, header, packet); /* return if handle decides we're done */ if (done) return; } /* parse payload */ switch (ethernet->ether_type) { case (0x0008): dp_parse_ip (handle, header, payload); break; case (0xDD86): dp_parse_ip6 (handle, header, payload); break; default: // TODO: maybe support for other protocols apart from IPv4 and IPv6 break; } } /* ppp header, i hope ;) */ /* glanced from ethereal, it's 16 bytes, and the payload packet type is * in the last 2 bytes... */ struct ppp_header { u_int16_t dummy1; u_int16_t dummy2; u_int16_t dummy3; u_int16_t dummy4; u_int16_t dummy5; u_int16_t dummy6; u_int16_t dummy7; u_int16_t packettype; }; void dp_parse_ppp (struct dp_handle * handle, const dp_header * header, const u_char * packet) { const struct ppp_header * ppp = (struct ppp_header *) packet; u_char * payload = (u_char *) packet + sizeof (struct ppp_header); /* call handle if it exists */ if (handle->callback[dp_packet_ppp] != NULL) { int done = (handle->callback[dp_packet_ppp]) (handle->userdata, header, packet); /* return if handle decides we're done */ if (done) return; } /* parse payload */ switch (ppp->packettype) { case (0x0008): dp_parse_ip (handle, header, payload); break; case (0xDD86): dp_parse_ip6 (handle, header, payload); break; default: // TODO: support for other than IPv4 and IPv6 break; } } /* linux cooked header, i hope ;) */ /* glanced from libpcap/ssl.h */ #define SLL_ADDRLEN 8 /* length of address field */ struct sll_header { u_int16_t sll_pkttype; /* packet type */ u_int16_t sll_hatype; /* link-layer address type */ u_int16_t sll_halen; /* link-layer address length */ u_int8_t sll_addr[SLL_ADDRLEN]; /* link-layer address */ u_int16_t sll_protocol; /* protocol */ }; void dp_parse_linux_cooked (struct dp_handle * handle, const dp_header * header, const u_char * packet) { const struct sll_header * sll = (struct sll_header *) packet; u_char * payload = (u_char *) packet + sizeof (struct sll_header); /* call handle if it exists */ if (handle->callback[dp_packet_sll] != NULL) { int done = (handle->callback[dp_packet_sll]) (handle->userdata, header, packet); /* return if handle decides we're done */ if (done) return; } /* parse payload */ switch (sll->sll_protocol) { case (0x0008): dp_parse_ip (handle, header, payload); break; case (0xDD86): dp_parse_ip6 (handle, header, payload); break; default: // TODO: support for other than IPv4 / IPv6 break; } } /* functions to do the monitoring */ void dp_pcap_callback (u_char * u_handle, const struct pcap_pkthdr * header, const u_char * packet) { struct dp_handle * handle = (struct dp_handle *) u_handle; struct dp_header; /* make a copy of the userdata for every packet */ u_char * userdata_copy = (u_char *) malloc (handle->userdata_size); memcpy (userdata_copy, handle->userdata, handle->userdata_size); switch (handle->linktype) { case (DLT_EN10MB): dp_parse_ethernet (handle, header, packet); break; case (DLT_PPP): dp_parse_ppp (handle, header, packet); break; case (DLT_LINUX_SLL): dp_parse_linux_cooked (handle, header, packet); break; case (DLT_RAW): case (DLT_NULL): // hope for the best dp_parse_ip (handle, header, packet); break; default: fprintf(stdout, "Unknown linktype %d", handle->linktype); break; } free (userdata_copy); } int dp_dispatch (struct dp_handle * handle, int count, u_char *user, int size) { handle->userdata = user; handle->userdata_size = size; return pcap_dispatch (handle->pcap_handle, count, dp_pcap_callback, (u_char *)handle); } int dp_setnonblock (struct dp_handle * handle, int i, char * errbuf) { return pcap_setnonblock (handle->pcap_handle, i, errbuf); } char * dp_geterr (struct dp_handle * handle) { return pcap_geterr (handle->pcap_handle); } nethogs/decpcap.h0000644000175000017500000000264011607136505014553 0ustar arnoutenarnouten#include #include #include #define DP_ERRBUF_SIZE PCAP_ERRBUF_SIZE /* definitions */ enum dp_packet_type { dp_packet_ethernet, dp_packet_ppp, dp_packet_sll, dp_packet_ip, dp_packet_ip6, dp_packet_tcp, dp_packet_udp, dp_n_packet_types }; /*enum dp_link_type { dp_link_ethernet, dp_link_ppp, dp_n_link_types };*/ /*struct dp_header { * pcap };*/ typedef struct pcap_pkthdr dp_header; typedef int (*dp_callback)(u_char *, const dp_header *, const u_char *); struct dp_handle { pcap_t * pcap_handle; dp_callback callback [dp_n_packet_types]; int linktype; u_char * userdata; int userdata_size; }; /* functions to set up a handle (which is basically just a pcap handle) */ struct dp_handle * dp_open_live(const char * device, int snaplen, int promisc, int to_ms, const char * ebuf); struct dp_handle * dp_open_offline(char * fname, char * ebuf); /* functions to add callbacks */ void dp_addcb (struct dp_handle * handle, enum dp_packet_type type, dp_callback callback); /* functions to parse payloads */ void dp_parse (enum dp_packet_type type, void * packet); /* functions to start monitoring */ int dp_dispatch (struct dp_handle * handler, int count, u_char *user, int size); /* functions that simply call libpcap */ int dp_datalink(struct dp_handle * handle); int dp_setnonblock (struct dp_handle * handle, int i, char * errbuf); char * dp_geterr (struct dp_handle * handle); nethogs/decpcap_test0000755000175000017500000006745311626170136015403 0ustar arnoutenarnoutenELF4\4 ('$444444LHHHDDPtdQtd/lib/ld-linux.so.2GNUGNU)*,D.\W[*    # )K! (EL C^  lD{P3O7 libpcap.so.0.8__gmon_start___Jv_RegisterClassespcap_setnonblockpcap_geterrpcap_datalinkpcap_dispatchpcap_open_livepcap_open_offlinelibstdc++.so.6_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6__ZSt4cout_Znaj_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc_ZNSt8ios_base4InitC1Ev_ZNSt8ios_base4InitD1Ev_ZNSolsEPFRSoS_Elibm.so.6libgcc_s.so.1libc.so.6_IO_stdin_used__cxa_atexitstdoutmemcpymallocfwritefprintf__libc_start_mainfreeGLIBCXX_3.4GLIBC_2.0GLIBC_2.1.3 t)xii si  ĦȦ̦ Ц Ԧ ئ ܦ US[\t> X[5%%h%h%h%h%h %h(%Ħh0%Ȧh8p%̦h@`%ЦhHP%ԦhP@%ئhX0%ܦh` %hh%hp%hx%h%h%h%h1^PTRhhQVhUS=ħu?ȧ됥9sȧȧ9rħ[]Ít&'Utt $ÐUD$$ jD$$US }$D$ڑ$ /D$$$sD$E T$T$$D$D$tD$D$$LD$ D$D$D$$3D$|$u.D$$|D$$ \$$z]U}u2} u)$̧D$D$̧$UD$$U($,EEUEEUDE}~E$ UB E@ t4qtYu~¸T$ D$D$$u¸ T$ D$D$$K¸4T$ D$"D$$!EH WL$T$$EU(E D$E$E}u E$U8ED$ED$ ED$E D$E$hE}u E$yUEU ML]U(E@t'EHE@$UT$U T$$щE}U(EEEEE@t(EHE@$UT$U T$$щE}u-E@ uED$E D$E$XU(EEE(EE@t(EHE@$UT$U T$$щE}u-E@uED$E D$E$U(EEEEE@t(EHE@$UT$U T$$щE}uQE@ t =t6ED$E D$E$ED$E D$E$U(EEEEE@t(EHE@$UT$U T$$щE}uQE@t =t6ED$E D$E$ED$E D$E$[U(EEEEE@ t(EH E@$UT$U T$$щE}uQE@t =t6ED$E D$E$dED$E D$E$U(EEE@($EE@(E@$‹EL$T$$qE@ t7  tdtx tXqt8lED$E D$E$rED$E D$E$/WED$E D$E$N^n~GCC: (Debian 4.6.1-3) 4.6.1GCC: (Debian 4.4.6-6) 4.4.6tpl h h t #" #&## # ##7 #i# #$#(J #,+"#0y $#4W&#8 *#< ,#@0#D1#F02#G6#H ?#LH)#TI)#XJ)#\qK)#` L #doN#h. P#lA%x(  - aTs WV Y ]s WU# g^7#  int_+j stdB0Mk!Nk%Ln $ @ \ 4 L m     " F h Ñ ĺ    7 Y { ˔ ̭    /       1i  7E 8 9u z   + 'z y  T U V ^ g/ jK kb$]rz< @>]% K _! 7 ! !!! !!  %!%!+ IM!! `Pk!!%" y!!%" 8 !# v !! ! k !!! ! <g!  H!!H! ! k!!  !  Z!! !%" 5!%!%" !! _b!!%! %!!%!  oL!! !%! 4n!%!%! j!%! #!%!  p !!!$!!%$`  !%!%$$ !%!%$@ !!%$  \ !%!% gU  !! !%!   %tm,4 # K # _# 9# C# # # a#  # o  #$ ` #(   L !%$m !!%! $Q !%!%! $/  !!%!    !! ! !%$  !%!% <   !%!  ? ? !%! ~ R h !!%! Y   !%! !    !%! !$(  !!%!   c ! D B !%!%!  G7 !!%!  LY !!%!   P{ !!!  aW !%"  !%"&ss% !%!'C C % !%!%&% !%!'%/ !%!%'D9D%U !%!! (    *}+ _   !%!   O   !%! ! .  !%! !  L($8<)9p*w+Z7z!!,eq '!!,lt='!!' !.!.!}'p}!.' . !.!}!' 4F!4!.!}'! > 4l!4!.!}'Z4!4!}!H'H!:'uS!'"'!:!:-eof&VS.4 *S!:/H/"HZ"H/@S0 86 a:# ;# %A# G# ? H# ]I# J# !K# L# QM#$ N#( O#) Q#* S#+ U#, W#- ^#. _#/ b#0 d#1 f#2 h#3 o#4 p#5$ }!!1Eg !)8        2  * 73f :3;3?3@$$'* 73f :3;3?3@$ $  +5 $3/!!$K!!$b!$5y!* 73f :3;3?3@$$* 73f :3;3?3@$"$"""i $0iKc M# N#4:0  # # 0#j%j%  c  'D/{Q015ts:# S#5len# $=$6W  777E777Y757L _8!1!!70 ,! "# # #  $# %1#$ &#(  9,tV:1::7;  8<4 < =~>Pt>t?rett@Ȋp<<AȊB)Cy̧DBoxEOAF Fl D~Gh s h 0 z B g !T Lint8w      "%|#7i $IT{0HBiK=M#N# M T ' )=#*=# + # I10D 2B4T 7#      T  T % T@@# P T' ip l1 oT#& pT# v# w# x#_ y#0 ~# # b #  #  #R A #^ !# "# ##  `  $ b %(   &A# '%# (%# D n p#" #& # #  # # #7 #i #  #$ #(J #,+ "#0y $#4W &p#8 *p#< ,#@ 0B#D 1b#F0 2#G 6#H ?#L H#T I#X J#\q K#`  L#do Np#h. P@#l:   # #0 p#u  Tn{ Q ts #S #len # !,W  nEY5L _p&n ,!"### $p# %#$&p#( y T  # # # # ## # * #   #>  #W #M  ##7  #  T>>W  >hipl .># . .+ 0l:>#mPf :k :p:pQ:p  :+ <lrHmB H>H1Hy$O O>BO9O& Vpln^/ ^>B^9^&ip` l1eh ipd P y/0 y>By9y&ip6{ l1|h pd ` Ah >B9&  l1h0 pd M A܎ >B9&ppp l1hH pd m ܎w >B9&sll l1h` pd  wx~ !i!B!9&" >l" h# 'pxH !'>!Y'p!]'!'p # -pؐ# !->$i-p!P-#1ؐQ !1>% % %  : ; : ;I8 : ; I I$ > : ;I : ; @  : ;  : ; I : ; I8 I !I/ $ > &I I9: ; : ; : ;9: ; < <  : ; : ;I? 2 < .? : ;< dI4 .? : ;< d< /I4: ; @I? < 4: ; I< .? : ;I< !I"#.? : ;I< $.? : ; I< % : ; &.? : ; @I< '.? : ;@I< (9: ; ):: ; *G : ; +.? : ; @< ,.? : ; @I< -.? : ;@I< ..? : ;@I< / I0 : ; 1.? : ; I< 2 3 : ; I? < 4: ; 5 : ; I8 6 : ; 7( 8I9.? : ; @I@:I ;.? : ; I@<: ; I = >4: ; I ?4: ; I @.4 @A.4 @B4I? 4 < C4G D4G@ E4G@F4G@ G4G@% : ; I$ > $ >   I : ;  : ; I8 I !I/  : ;  : ; I  : ;  : ; I 8 &I : ; : ;I8 : ; <  : ; I8  : ; ( ' II.? : ; ' I@: ; I 4: ; I 4: ; I .? : ; ' @  U .? : ;' @!: ;I "4: ;I #.? : ;' I@$: ;I %4: ; I? < ~ /usr/include/c++/4.6/usr/include/usr/lib/i386-linux-gnu/gcc/i486-linux-gnu/4.6.1/include/usr/include/c++/4.6/i486-linux-gnu/bits/usr/include/c++/4.6/bits/usr/include/c++/4.6/ext/usr/include/c++/4.6/debug/usr/include/bits/usr/include/sys/usr/include/pcapdecpcap_test.cppiostreamlibio.hstdio.hstdarg.hstddef.hwchar.hcwcharc++config.hclocaleios_base.hcwctypeiosfwdtime.hnew_allocator.hdebug.hchar_traits.hlocale.htypes.hatomic_word.hnumeric_traits.hwctype.htypes.h time.hbpf.h pcap.h decpcap.htg$Y1h$(v3Xff/Q).fLV /usr/include/bits/usr/include/sys/usr/lib/i386-linux-gnu/gcc/i486-linux-gnu/4.6.1/include/usr/include/net/usr/include/usr/include/netinet/usr/include/pcapdecpcap.ctypes.htypes.hstddef.htime.hethernet.hstdint.hin.hip.hip6.hbpf.hlibio.hpcap.hdecpcap.hstdio.hgJk0L(0(0(0#=1gLhw1g*hw3=3jgeZK#1gkgeZi1w< 1gggeZi1w< 1ggge\kw01r< .ggge\kw01r< .ggge\kw01r< 2gj#'0020"0g#0g1gdp_packet_ppp__debugint_p_cs_precedeswcsxfrm_shortbuf_IO_lock_tdp_packet_sllcaplenassigntm_yday_IO_buf_end_ZNSt11char_traitsIcE11to_int_typeERKcnewhandletv_secwcstoldvfwscanftowctrans_IO_write_end__gnu_cxx_flags__int32_tdecpcap_test.cppwchar_t_S_refcountdp_packet_ipvwscanf_markersvswscanf_ZNSt11char_traitsIcE2ltERKcS2_mon_decimal_point_ZN9__gnu_cxx24__numeric_traits_integerIiE5__minEpcapwcscpy_ZNSt11char_traitsIcE7not_eofERKiwcscat__numeric_traits_integerpcap_handledp_packet_ip6tm_yearwmemcpy__gnu_debug_posdp_packet_udptm_mon_IO_save_end__count__numeric_traits_integerfloat_cur_columnpositive_signbpf_u_int32long long unsigned intint_n_sign_posnlocaleconv__FILE_IO_backup_baseeq_int_typeto_int_typewcrtomb_ZSt4cout_GLOBAL__sub_I__Z11process_tcpPhPK11pcap_pkthdrPKherrbuf_filenovfwprintftimeval__pad4__gnuc_va_list__initialize_psize_t_vtable_offsetuserdata_size_IO_read_base__priority_nexttv_usecint_frac_digitswcsstrfwideint_n_cs_precedes__maxuserdatafindbasic_ostream >negative_signtm_hour__value_modeptrdiff_t_IO_marker_IO_write_base__wchmbsrtowcswcstoulwctrans_twcstofwcsspn_ZN9__gnu_cxx24__numeric_traits_integerIsE5__maxEwcsrtombslong long int_IO_save_basemon_groupingwcstoull_ZNSt11char_traitsIcE6assignERcRKcbooldp_headerchar_traitspcap_t__u_charfgetwsprocess_tcpcomparelong double__dso_handle_ZNSt11char_traitsIcE6assignEPcjc__pad1__pad2__pad3dp_packet_ethernet__pad5__is_signedungetwc_Valuewctype_targctm_mday_ZN9__gnu_cxx24__numeric_traits_integerIiE5__maxEwcschr_Traitsargvfputwcwcsncatwcscoll_S_synced_with_stdio_ZNSt11char_traitsIcE4moveEPcPKcjfputws__static_initialization_and_destruction_0ios_basebtowcvwprintfmbrtowc_IO_read_endiswctypembsinitwmemchrshort int_ZNSt11char_traitsIcE3eofEv_CharT~Initint_curr_symbol__digitsmbrlenlength11__mbstate_tpcap_pkthdrwcsrchrto_char_typegetwchar__wchbint_n_sep_by_space__numeric_traits_integer__quad_t_ZN9__gnu_cxx24__numeric_traits_integerIsE5__minEwcsncmpdp_packet_tcpwcsftime_ZNSt11char_traitsIcE6lengthEPKcint_p_sep_by_space_ZNSt11char_traitsIcE11eq_int_typeERKiS2__ZNSt11char_traitsIcE12to_char_typeERKiwmemmove_locklconv_old_offset_IO_FILE_Atomic_wordwint_tnot_eofwcstodwcspbrktm_minwcstokwcstoltm_zone/home/arnouten/dev/nethogswmemsetsetlocaleunsigned char_sbuf_IO_write_ptr_ZN9__gnu_cxx24__numeric_traits_integerImE8__digitsE__suseconds_tostream__time_tdp_handlewctob_ZNSt11char_traitsIcE4findEPKcjRS1_currency_symbolwcstolldp_packet_type__min_ZN9__gnu_cxx24__numeric_traits_integerIcE5__maxE__off_tputwcharmon_thousands_sepshort unsigned inttm_secmainwcscspn__builtin_va_listtm_wdayGNU C++ 4.6.1tm_isdstlinktype_IO_read_ptrwcsncpyfgetwc_ZNSt11char_traitsIcE4copyEPcPKcjwcscmp__u_inttm_gmtoff_chain__numeric_traits_integervswprintf_flags2_ZNSt11char_traitsIcE7compareEPKcS2_jint_p_sign_posn_ZNSt11char_traitsIcE2eqERKcS2_wcslen__off64_t__ioinit_unused2_IO_buf_basewmemcmpdp_n_packet_typesip6_un1_plensnaplenGNU C 4.6.1ether_dhostdonesll_pkttypeip_dstin_addr_tstdoutip6_un1_nxtdp_open_offline__u6_addr16__u6_addr32ip_srcretvalebufppp_headerdp_parse_pppip_puserdata_copyip_vtempip_ttlsll_protocoluint16_tsll_halenphandleip_offdevicedp_parse_linux_cookeddp_setnonblockdp_parse_ip6packettypeip6_un1_hlimdp_parse_ethernetfnameether_headerdp_dispatchip6_un1_flowether_typedummy1dummy2dummy3dummy4dummy5dummy6dummy7payloadpacket__in6_uu_int16_tip6_hdrctluserip_sumu_handledp_addcbu_int8_tip6_dstip_tossll_addrdp_callbackin6_addruint32_tdecpcap.cip6_srcip_len__u6_addr8dp_geterrether_shostip6_un1dp_open_liveip6_ctlundp_pcap_callbackdp_parse_tcpip_hls_addrsll_headeruint8_tto_mssll_hatypeip6_un2_vfcdp_parse_ip__u_shortip_idip6_hdrdp_fillhandlepromiscin_addrtt0u01t12t24t4utttSuSTtTUtUWtWouopttt u  t  t  t >u>?t?@t@BtButttutttutttJuJKtKLtLNtNuttt\u\]t]^t^`t`utttutttutttutttutttu tHIj Z[|?.symtab.strtab.shstrtab.interp.note.ABI-tag.note.gnu.build-id.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_r.rel.dyn.rel.plt.init.text.fini.rodata.eh_frame_hdr.eh_frame.init_array.ctors.dtors.jcr.dynamic.got.got.plt.data.bss.comment.debug_aranges.debug_info.debug_abbrev.debug_line.debug_str.debug_loc.debug_ranges44#HH 1hh$HDo<<4N ppV^o2ko00Pz   880hhP$$`\  08<@|$#T<1/B=0BGHVSZxZ[ab&: iW4Hh<p0   8 h $  !"# (5KħZȧhPt`̧@Ȋ4>Tex x=#  ;JmS_|܎ (95GY p}ؐZA5t1*=I[ i/wvЧ #JZ/w@E8 Kwcrtstuff.c__CTOR_LIST____DTOR_LIST____JCR_LIST____do_global_dtors_auxcompleted.5480dtor_idx.5482frame_dummy__CTOR_END____FRAME_END____JCR_END____do_global_ctors_auxdecpcap_test.cpp_ZStL8__ioinit_Z41__static_initialization_and_destruction_0ii_GLOBAL__sub_I__Z11process_tcpPhPK11pcap_pkthdrPKhdecpcap.c_GLOBAL_OFFSET_TABLE___init_array_end__init_array_start_DYNAMICdata_startdp_dispatchdp_setnonblock__cxa_atexit@@GLIBC_2.1.3__libc_csu_fini_startpcap_dispatch__gmon_start___Jv_RegisterClasses_fp_hw_fini_ZNSt8ios_base4InitC1Ev@@GLIBCXX_3.4pcap_open_livedp_addcbpcap_geterr__libc_start_main@@GLIBC_2.0dp_parse_linux_cooked_ZNSt8ios_base4InitD1Ev@@GLIBCXX_3.4_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@@GLIBCXX_3.4_IO_stdin_usedfree@@GLIBC_2.0__data_startdp_parse_tcppcap_open_offlinememcpy@@GLIBC_2.0_ZSt4cout@@GLIBCXX_3.4__dso_handledp_geterr__DTOR_END__pcap_datalink__libc_csu_initdp_parse_ethernetdp_parse_pppdp_open_offlinefwrite@@GLIBC_2.0_Znaj@@GLIBCXX_3.4_Z11process_tcpPhPK11pcap_pkthdrPKhfprintf@@GLIBC_2.0__bss_startmalloc@@GLIBC_2.0dp_fillhandledp_parse_ip6pcap_setnonblock_end_ZNSolsEPFRSoS_E@@GLIBCXX_3.4stdout@@GLIBC_2.0_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@@GLIBCXX_3.4_edatadp_open_live__i686.get_pc_thunk.bxdp_pcap_callbackmain_initdp_parse_ipnethogs/decpcap_test.cpp0000644000175000017500000000121711607135277016151 0ustar arnoutenarnouten#include extern "C" { #include "decpcap.h" } int process_tcp (u_char * /* userdata */, const dp_header * /* header */, const u_char * /* m_packet */) { std::cout << "Callback for processing TCP packet called" << std::endl; return 0; } int main (int argc, char ** argv) { if (argc < 2) { std::cout << "Please, enter a filename" << std::endl; } char* errbuf = new char[DP_ERRBUF_SIZE]; dp_handle * newhandle = dp_open_offline(argv[1], errbuf); dp_addcb (newhandle, dp_packet_tcp, process_tcp); int ret = dp_dispatch (newhandle, -1, NULL, 0); if (ret == -1) { std::cout << "Error dispatching: " << dp_geterr(newhandle); } } nethogs/DESIGN0000644000175000017500000000226710122035100013660 0ustar arnoutenarnoutenRough design notitions: decpcap handles pcap. nethogs.cpp asks to be notified of all IPv4 or IPv6 TCP packets. the IP callbacks store the source and destination addresses into the user data. The TCP callback makes a Packet out of it, finds the Connection corresponding to that packet, and adds the packet to the connection. If no connection is found, a new one is constructed, and the process related to that connection is found though getProcess. If needed, the screen is refreshed. If, in getProcess, no corresponding process is found, the connection is added to the 'unknown' process. To prevent connections from accidentally ending up in the 'unknown' process, and then staying there indefinitely, we should maybe walk though the unknownproc's connections whenever the connection-to-inode table is refreshed. And maybe, while there are still unknown connections, the connection-to-inode table should be updated regularly. There are some global data structures: connection.cpp: connections. 'ConnList' list containting all currently known connections. A connection removes itself from the global 'connections' list in its destructor. processes. 'ProcList *' containing all processes. nethogs/devices.cpp0000644000175000017500000000013311607140054015116 0ustar arnoutenarnouten#include "devices.h" device * determine_default_device() { return new device("eth0"); } nethogs/devices.h0000644000175000017500000000033511607137743014602 0ustar arnoutenarnouten#include // NULL class device { public: device (const char * m_name, device * m_next = NULL) { name = m_name; next = m_next; } const char * name; device * next; }; device * determine_default_device(); nethogs/inode2prog.cpp0000644000175000017500000001212511156312731015552 0ustar arnoutenarnouten#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "inode2prog.h" extern bool bughuntmode; /* maps from inode to program-struct */ std::map inodeproc; bool is_number (char * string) { while (*string) { if (!isdigit (*string)) return false; string++; } return true; } unsigned long str2ulong (char * ptr) { unsigned long retval = 0; while ((*ptr >= '0') && (*ptr <= '9')) { retval *= 10; retval += *ptr - '0'; ptr++; } return retval; } int str2int (char * ptr) { int retval = 0; while ((*ptr >= '0') && (*ptr <= '9')) { retval *= 10; retval += *ptr - '0'; ptr++; } return retval; } char * getprogname (char * pid) { int filenamelen = 14 + strlen(pid) + 1; int bufsize = 80; char buffer [bufsize]; char * filename = (char *) malloc (filenamelen); snprintf (filename, filenamelen, "/proc/%s/cmdline", pid); int fd = open(filename, O_RDONLY); if (fd < 0) { fprintf (stderr, "Error opening %s: %s\n", filename, strerror(errno)); free (filename); exit(3); return NULL; } int length = read (fd, buffer, bufsize); if (close (fd)) { std::cout << "Error closing file: " << strerror(errno) << std::endl; exit(34); } free (filename); if (length < bufsize - 1) buffer[length]='\0'; char * retval = buffer; /* this removed directory names, but that malfunctions * when the program name is like "sshd: arnouten@pts/8" if ((retval = strrchr(buffer, '/'))) retval++; else retval = buffer; */ // truncating is now done where it should be, in cui.cpp return strdup(retval); } void setnode (unsigned long inode, prg_node * newnode) { if (inodeproc[inode] != NULL) free (inodeproc[inode]); inodeproc[inode] = newnode; } void get_info_by_linkname (char * pid, char * linkname) { if (strncmp(linkname, "socket:[", 8) == 0) { char * ptr = linkname + 8; unsigned long inode = str2ulong(ptr); char * progname = getprogname (pid); //std::cout << "Found socket with inode " << inode << " and pid " << pid << " and progname " << progname << "\n"; prg_node * newnode = (prg_node *) malloc (sizeof (struct prg_node)); newnode->inode = inode; newnode->pid = str2int(pid); // TODO progname could be more memory-efficient strncpy (newnode->name, progname, PROGNAME_WIDTH); free (progname); setnode (inode, newnode); } else { //std::cout << "Linkname looked like: " << linkname << endl; } } /* updates the `inodeproc' inode-to-prg_node * for all inodes belonging to this PID * (/proc/pid/fd/42) * */ void get_info_for_pid(char * pid) { size_t dirlen = 10 + strlen(pid); char * dirname = (char *) malloc (dirlen * sizeof(char)); snprintf(dirname, dirlen, "/proc/%s/fd", pid); //std::cout << "Getting info for pid " << pid << std::endl; DIR * dir = opendir(dirname); if (!dir) { std::cout << "Couldn't open dir " << dirname << ": " << strerror(errno) << "\n"; free (dirname); return; } /* walk through /proc/%s/fd/... */ dirent * entry; while ((entry = readdir(dir))) { if (entry->d_type != DT_LNK) continue; //std::cout << "Looking at: " << entry->d_name << std::endl; int fromlen = dirlen + strlen(entry->d_name) + 1; char * fromname = (char *) malloc (fromlen * sizeof(char)); snprintf (fromname, fromlen, "%s/%s", dirname, entry->d_name); //std::cout << "Linking from: " << fromname << std::endl; int linklen = 80; char linkname [linklen]; int usedlen = readlink(fromname, linkname, linklen-1); if (usedlen == -1) { free (fromname); continue; } assert (usedlen < linklen); linkname[usedlen] = '\0'; //std::cout << "Linking to: " << linkname << std::endl; get_info_by_linkname (pid, linkname); free (fromname); } closedir(dir); free (dirname); } /* updates the `inodeproc' inode-to-prg_node mapping * for all processes in /proc */ void reread_mapping () { DIR * proc = opendir ("/proc"); if (proc == 0) { std::cerr << "Error reading /proc, neede to get inode-to-pid-maping\n"; exit(1); } dirent * entry; while ((entry = readdir(proc))) { if (entry->d_type != DT_DIR) continue; if (! is_number (entry->d_name)) continue; //std::cout << "Getting info for " << entry->d_name << std::endl; get_info_for_pid(entry->d_name); } //std::cout << "End...\n"; closedir(proc); } struct prg_node * findPID (unsigned long inode) { /* we first look in inodeproc */ struct prg_node * node = inodeproc[inode]; if (node != NULL) { if (bughuntmode) { std::cout << ":) Found pid in inodeproc table" << std::endl; } return node; } reread_mapping(); struct prg_node * retval = inodeproc[inode]; if (bughuntmode) { if (retval == NULL) { std::cout << ":( No pid after inodeproc refresh" << std::endl; } else { std::cout << ":) Found pid after inodeproc refresh" << std::endl; } } return retval; } void prg_cache_clear() {}; /*void main () { std::cout << "Fooo\n"; reread_mapping(); std::cout << "Haihai\n"; }*/ nethogs/inode2prog.h0000644000175000017500000000062411030251205015205 0ustar arnoutenarnouten/* this should be called quickly after the packet * arrived, since the inode may disappear from the table * quickly, too :) */ #include "nethogs.h" // #define PROGNAME_WIDTH 200 struct prg_node { long inode; int pid; char name[PROGNAME_WIDTH]; }; struct prg_node * findPID (unsigned long inode); void prg_cache_clear(); // reread the inode-to-prg_node-mapping void reread_mapping (); nethogs/INSTALL0000644000175000017500000000021210070267430014020 0ustar arnoutenarnoutenmake ; make install you need the 'libpcap-dev' and 'libpcap' packages. let me know if you have any other problems on nethogs@bzzt.net nethogs/Makefile0000644000175000017500000000373111626171536014451 0ustar arnoutenarnoutenVERSION := 0 SUBVERSION := 8 MINORVERSION := 0 #DESTDIR := /usr DESTDIR := /usr/local sbin := $(DESTDIR)/sbin man8 := $(DESTDIR)/share/man/man8/ all: nethogs decpcap_test # nethogs_testsum CFLAGS=-g -Wall -Wextra #CFLAGS=-O2 OBJS=packet.o connection.o process.o refresh.o decpcap.o cui.o inode2prog.o conninode.o devices.o .PHONY: tgz tgz: clean cd .. ; tar czvf nethogs-$(VERSION).$(SUBVERSION).$(MINORVERSION).tar.gz --exclude-vcs nethogs/* .PHONY: check check: echo "Not implemented" install: nethogs nethogs.8 install -d -m 755 $(sbin) install -m 755 nethogs $(sbin) install -d -m 755 $(man8) install -m 644 nethogs.8 $(man8) nethogs: nethogs.cpp $(OBJS) $(CXX) $(CFLAGS) nethogs.cpp $(OBJS) -o nethogs -lpcap -lm -lncurses -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" nethogs_testsum: nethogs_testsum.cpp $(OBJS) $(CXX) $(CFLAGS) -g nethogs_testsum.cpp $(OBJS) -o nethogs_testsum -lpcap -lm -lncurses -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" decpcap_test: decpcap_test.cpp decpcap.o $(CXX) $(CFLAGS) decpcap_test.cpp decpcap.o -o decpcap_test -lpcap -lm #-lefence refresh.o: refresh.cpp refresh.h nethogs.h $(CXX) $(CFLAGS) -c refresh.cpp process.o: process.cpp process.h nethogs.h $(CXX) $(CFLAGS) -c process.cpp packet.o: packet.cpp packet.h nethogs.h $(CXX) $(CFLAGS) -c packet.cpp connection.o: connection.cpp connection.h nethogs.h $(CXX) $(CFLAGS) -c connection.cpp decpcap.o: decpcap.c decpcap.h $(CC) $(CFLAGS) -c decpcap.c inode2prog.o: inode2prog.cpp inode2prog.h nethogs.h $(CXX) $(CFLAGS) -c inode2prog.cpp conninode.o: conninode.cpp nethogs.h conninode.h $(CXX) $(CFLAGS) -c conninode.cpp #devices.o: devices.cpp devices.h # $(CXX) $(CFLAGS) -c devices.cpp cui.o: cui.cpp cui.h nethogs.h $(CXX) $(CFLAGS) -c cui.cpp -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" .PHONY: clean clean: rm -f $(OBJS) rm -f nethogs nethogs/nethogs.80000644000175000017500000000237411356131033014537 0ustar arnoutenarnouten.\" This page Copyright (C) 2004 Fabian Frederick .\" Content based on Nethogs homepage by Arnout Engelen .TH NETHOGS 8 "14 February 2004" .SH NAME nethogs \- Net top tool grouping bandwidth per process .SH SYNOPSIS .ft B .B nethogs .RB [ "\-d" ] .RB [ "\-h" ] .RB [ "\-p" ] .RB [ "\-t" ] .RB [ "\-V" ] .RI [device(s)] .SH DESCRIPTION NetHogs is a small 'net top' tool. Instead of breaking the traffic down per protocol or per subnet, like most such tools do, it groups bandwidth by process - and does not rely on a special kernel module to be loaded. So if there's suddenly a lot of network traffic, you can fire up NetHogs and immediately see which PID is causing this, and if it's some kind of spinning process, kill it. .SS Options .TP \fB-d\fP delay for refresh rate. .TP \fB-h\fP display available commands usage. .TP \fB-p\fP sniff in promiscious mode (not recommended). .TP \fB-t\fP tracemode. .TP \fB-V\fP prints Version info. .PP .I device(s) to monitor. By default eth0 is being used. .SH "INTERACTIVE CONTROL" .TP m cycle between display modes (kb/s, kb, b, mb) .TP r sort by 'received' .TP s sort by 'sent' .TP q quit .RE .SH "SEE ALSO" .I netstat(8) tcpdump(1) pcap(3) .SH AUTHOR .nf Written by Arnout Engelen . nethogs/nethogs.cpp0000644000175000017500000002253211607137375015166 0ustar arnoutenarnouten/* nethogs.cpp */ #include "nethogs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cui.h" extern "C" { #include "decpcap.h" } #include "packet.h" #include "connection.h" #include "process.h" #include "refresh.h" #include "devices.h" extern Process * unknownudp; unsigned refreshdelay = 1; bool tracemode = false; bool bughuntmode = false; bool needrefresh = true; //packet_type packettype = packet_ethernet; //dp_link_type linktype = dp_link_ethernet; const char version[] = " version " VERSION "." SUBVERSION "." MINORVERSION; const char * currentdevice = NULL; timeval curtime; bool local_addr::contains (const in_addr_t & n_addr) { if ((sa_family == AF_INET) && (n_addr == addr)) return true; if (next == NULL) return false; return next->contains(n_addr); } bool local_addr::contains(const struct in6_addr & n_addr) { if (sa_family == AF_INET6) { /* if (DEBUG) { char addy [50]; std::cerr << "Comparing: "; inet_ntop (AF_INET6, &n_addr, addy, 49); std::cerr << addy << " and "; inet_ntop (AF_INET6, &addr6, addy, 49); std::cerr << addy << std::endl; } */ //if (addr6.s6_addr == n_addr.s6_addr) if (memcmp (&addr6, &n_addr, sizeof(struct in6_addr)) == 0) { if (DEBUG) std::cerr << "Match!" << std::endl; return true; } } if (next == NULL) return false; return next->contains(n_addr); } struct dpargs { int sa_family; in_addr ip_src; in_addr ip_dst; in6_addr ip6_src; in6_addr ip6_dst; }; int process_tcp (u_char * userdata, const dp_header * header, const u_char * m_packet) { struct dpargs * args = (struct dpargs *) userdata; struct tcphdr * tcp = (struct tcphdr *) m_packet; curtime = header->ts; /* get info from userdata, then call getPacket */ Packet * packet; switch (args->sa_family) { case (AF_INET): packet = new Packet (args->ip_src, ntohs(tcp->source), args->ip_dst, ntohs(tcp->dest), header->len, header->ts); break; case (AF_INET6): packet = new Packet (args->ip6_src, ntohs(tcp->source), args->ip6_dst, ntohs(tcp->dest), header->len, header->ts); break; } Connection * connection = findConnection(packet); if (connection != NULL) { /* add packet to the connection */ connection->add(packet); } else { /* else: unknown connection, create new */ connection = new Connection (packet); getProcess(connection, currentdevice); } delete packet; if (needrefresh) { do_refresh(); needrefresh = false; } /* we're done now. */ return true; } int process_udp (u_char * userdata, const dp_header * header, const u_char * m_packet) { struct dpargs * args = (struct dpargs *) userdata; //struct tcphdr * tcp = (struct tcphdr *) m_packet; struct udphdr * udp = (struct udphdr *) m_packet; curtime = header->ts; /* TODO get info from userdata, then call getPacket */ Packet * packet; switch (args->sa_family) { case (AF_INET): packet = new Packet (args->ip_src, ntohs(udp->source), args->ip_dst, ntohs(udp->dest), header->len, header->ts); break; case (AF_INET6): packet = new Packet (args->ip6_src, ntohs(udp->source), args->ip6_dst, ntohs(udp->dest), header->len, header->ts); break; } //if (DEBUG) // std::cout << "Got packet from " << packet->gethashstring() << std::endl; Connection * connection = findConnection(packet); if (connection != NULL) { /* add packet to the connection */ connection->add(packet); } else { /* else: unknown connection, create new */ connection = new Connection (packet); getProcess(connection, currentdevice); } delete packet; if (needrefresh) { do_refresh(); needrefresh = false; } /* we're done now. */ return true; } int process_ip (u_char * userdata, const dp_header * /* header */, const u_char * m_packet) { struct dpargs * args = (struct dpargs *) userdata; struct ip * ip = (struct ip *) m_packet; args->sa_family = AF_INET; args->ip_src = ip->ip_src; args->ip_dst = ip->ip_dst; /* we're not done yet - also parse tcp :) */ return false; } int process_ip6 (u_char * userdata, const dp_header * /* header */, const u_char * m_packet) { struct dpargs * args = (struct dpargs *) userdata; const struct ip6_hdr * ip6 = (struct ip6_hdr *) m_packet; args->sa_family = AF_INET6; args->ip6_src = ip6->ip6_src; args->ip6_dst = ip6->ip6_dst; /* we're not done yet - also parse tcp :) */ return false; } void quit_cb (int /* i */) { procclean(); if ((!tracemode) && (!DEBUG)) exit_ui(); exit(0); } void forceExit(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; exit(0); } static void versiondisplay(void) { std::cerr << version << "\n"; } static void help(void) { //std::cerr << "usage: nethogs [-V] [-b] [-d seconds] [-t] [-p] [-f (eth|ppp))] [device [device [device ...]]]\n"; std::cerr << "usage: nethogs [-V] [-b] [-d seconds] [-t] [-p] [device [device [device ...]]]\n"; std::cerr << " -V : prints version.\n"; std::cerr << " -d : delay for update refresh rate in seconds. default is 1.\n"; std::cerr << " -t : tracemode.\n"; //std::cerr << " -f : format of packets on interface, default is eth.\n"; std::cerr << " -b : bughunt mode - implies tracemode.\n"; std::cerr << " -p : sniff in promiscious mode (not recommended).\n"; std::cerr << " device : device(s) to monitor. default is eth0\n"; std::cerr << std::endl; std::cerr << "When nethogs is running, press:\n"; std::cerr << " q: quit\n"; std::cerr << " m: switch between total and kb/s mode\n"; } class handle { public: handle (dp_handle * m_handle, const char * m_devicename = NULL, handle * m_next = NULL) { content = m_handle; next = m_next; devicename = m_devicename; } dp_handle * content; const char * devicename; handle * next; }; int main (int argc, char** argv) { process_init(); device * devices = NULL; //dp_link_type linktype = dp_link_ethernet; int promisc = 0; int opt; while ((opt = getopt(argc, argv, "Vhbtpd:")) != -1) { switch(opt) { case 'V': versiondisplay(); exit(0); case 'h': help(); exit(0); case 'b': bughuntmode = true; tracemode = true; break; case 't': tracemode = true; break; case 'p': promisc = 1; break; case 'd': refreshdelay=atoi(optarg); break; /* case 'f': argv++; if (strcmp (optarg, "ppp") == 0) linktype = dp_link_ppp; else if (strcmp (optarg, "eth") == 0) linktype = dp_link_ethernet; } break; */ default: help(); exit(EXIT_FAILURE); } } while (optind < argc) { devices = new device (strdup(argv[optind++]), devices); } if (devices == NULL) { devices = determine_default_device(); } if ((!tracemode) && (!DEBUG)){ init_ui(); } if (NEEDROOT && (getuid() != 0)) forceExit("You need to be root to run NetHogs!"); char errbuf[PCAP_ERRBUF_SIZE]; handle * handles = NULL; device * current_dev = devices; while (current_dev != NULL) { getLocal(current_dev->name, tracemode); if ((!tracemode) && (!DEBUG)){ //caption->append(current_dev->name); //caption->append(" "); } 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\n", current_dev->name); } current_dev = current_dev->next; } signal (SIGALRM, &alarm_cb); signal (SIGINT, &quit_cb); alarm (refreshdelay); fprintf(stderr, "Waiting for first packet to arrive (see sourceforge.net bug 1019381)\n"); // 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 (1) { bool packets_read = false; handle * current_handle = handles; while (current_handle != NULL) { struct dpargs * userdata = (dpargs *) malloc (sizeof (struct dpargs)); userdata->sa_family = AF_UNSPEC; currentdevice = current_handle->devicename; int retval = dp_dispatch (current_handle->content, -1, (u_char *)userdata, sizeof (struct dpargs)); if (retval == -1 || retval == -2) { std::cerr << "Error dispatching" << std::endl; } else if (retval != 0) { packets_read = true; } free (userdata); current_handle = current_handle->next; } if ((!DEBUG)&&(!tracemode)) { // handle user input ui_tick(); } if (needrefresh) { do_refresh(); needrefresh = false; } // If no packets were read at all this iteration, pause to prevent 100% // CPU utilisation; if (!packets_read) { usleep(100); } } } nethogs/nethogs.h0000644000175000017500000000572611437270451014633 0ustar arnoutenarnouten#ifndef __NETHOGS_H #define __NETHOGS_H #include #include #include #include #include #include #include #include #define _BSD_SOURCE 1 /* take the average speed over the last 5 seconds */ #define PERIOD 5 /* the amount of time after the last packet was recieved * after which a process is removed */ #define PROCESSTIMEOUT 150 /* the amount of time after the last packet was recieved * after which a connection is removed */ #define CONNTIMEOUT 50 /* Set to '0' when compiling for a system that uses Linux Capabilities, * like www.adamantix.org: in that case nethogs shouldn't check if it's * running as root. Take care to give it sufficient privileges though. */ #ifndef NEEDROOT #define NEEDROOT 1 #endif #define DEBUG 0 #define REVERSEHACK 0 // 2 times: 32 characters, 7 ':''s, a ':12345'. // 1 '-' // -> 2*45+1=91. we make it 92, for the null. #define HASHKEYSIZE 92 #define PROGNAME_WIDTH 512 void forceExit(const char *msg, ...); class local_addr { public: /* ipv4 constructor takes an in_addr_t */ local_addr (in_addr_t m_addr, local_addr * m_next = NULL) { addr = m_addr; next = m_next; sa_family = AF_INET; string = (char*) malloc (16); inet_ntop (AF_INET, &m_addr, string, 15); } /* this constructor takes an char address[33] */ local_addr (char m_address [33], local_addr * m_next = NULL) { next = m_next; char address [40]; address[0] = m_address[0]; address[1] = m_address[1]; address[2] = m_address[2]; address[3] = m_address[3]; address[4] = ':'; address[5] = m_address[4]; address[6] = m_address[5]; address[7] = m_address[6]; address[8] = m_address[7]; address[9] = ':'; address[10] = m_address[8]; address[11] = m_address[9]; address[12] = m_address[10]; address[13] = m_address[11]; address[14] = ':'; address[15] = m_address[12]; address[16] = m_address[13]; address[17] = m_address[14]; address[18] = m_address[15]; address[19] = ':'; address[20] = m_address[16]; address[21] = m_address[17]; address[22] = m_address[18]; address[23] = m_address[19]; address[24] = ':'; address[25] = m_address[20]; address[26] = m_address[21]; address[27] = m_address[22]; address[28] = m_address[23]; address[29] = ':'; address[30] = m_address[24]; address[31] = m_address[25]; address[32] = m_address[26]; address[33] = m_address[27]; address[34] = ':'; address[35] = m_address[28]; address[36] = m_address[29]; address[37] = m_address[30]; address[38] = m_address[31]; address[39] = 0; string = strdup(address); //if (DEBUG) // std::cout << "Converting address " << address << std::endl; int result = inet_pton (AF_INET6, address, &addr6); assert (result > 0); sa_family = AF_INET6; } bool contains (const in_addr_t & n_addr); bool contains (const struct in6_addr & n_addr); char * string; local_addr * next; private: in_addr_t addr; struct in6_addr addr6; short int sa_family; }; void quit_cb (int i); #endif nethogs/packet.cpp0000644000175000017500000001600711126712472014757 0ustar arnoutenarnouten#include "nethogs.h" #include #include "packet.h" #include #include #include #include #include #include #include #include #include #include // #include "inet6.c" local_addr * local_addrs = NULL; /* moves the pointer right until a non-space is seen */ char * stripspaces (char * input) { char * retval = input; while (*retval == ' ') retval++; return retval; } /* * getLocal * device: This should be device explicit (e.g. eth0:1) * * uses ioctl to get address of this device, and adds it to the * local_addrs-list. */ void getLocal (const char *device, bool tracemode) { /* get local IPv4 addresses */ int sock; struct ifreq iFreq; struct sockaddr_in *saddr; if((sock=socket(AF_INET, SOCK_RAW, htons(0x0806)))<0){ forceExit("creating socket failed while establishing local IP - are you root?"); } strcpy(iFreq.ifr_name, device); if(ioctl(sock, SIOCGIFADDR, &iFreq)<0){ forceExit("ioctl failed while establishing local IP for selected device %s. You may specify the device on the command line.", device); } saddr=(struct sockaddr_in*)&iFreq.ifr_addr; local_addrs = new local_addr (saddr->sin_addr.s_addr, local_addrs); if (tracemode || DEBUG) { printf ("Adding local address: %s\n", inet_ntoa(saddr->sin_addr)); } /* also get local IPv6 addresses */ FILE * ifinfo = fopen ("/proc/net/if_inet6", "r"); char buffer [500]; if (ifinfo) { do { if (fgets(buffer, sizeof(buffer), ifinfo)) { char address [33]; char ifname [9]; int n_results = sscanf (buffer, "%32[0-9a-f] %*d %*d %*d %*d %8[0-9a-zA-Z]", address, ifname); assert (n_results = 2); if (strcmp (stripspaces(ifname), device) == 0) { local_addrs = new local_addr (address, local_addrs); } #if DEBUG else { std::cerr << "Address skipped for interface " << ifname << std::endl; } #endif } } while (!feof(ifinfo)); fclose(ifinfo); } } typedef u_int32_t tcp_seq; /* ppp header, i hope ;) */ /* glanced from ethereal, it's 16 bytes, and the payload packet type is * in the last 2 bytes... */ struct ppp_header { u_int16_t dummy1; u_int16_t dummy2; u_int16_t dummy3; u_int16_t dummy4; u_int16_t dummy5; u_int16_t dummy6; u_int16_t dummy7; u_int16_t packettype; }; /* TCP header */ // TODO take from elsewhere. struct tcp_hdr { u_short th_sport; /* source port */ u_short th_dport; /* destination port */ tcp_seq th_seq; /* sequence number */ tcp_seq th_ack; /* acknowledgement number */ #if BYTE_ORDER == LITTLE_ENDIAN u_int th_x2:4, /* (unused) */ th_off:4; /* data offset */ #endif #if BYTE_ORDER == BIG_ENDIAN u_int th_off:4, /* data offset */ th_x2:4; /* (unused) */ #endif u_char th_flags; #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 #define TH_ECE 0x40 #define TH_CWR 0x80 #define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR) u_short th_win; /* window */ u_short th_sum; /* checksum */ u_short th_urp; /* urgent pointer */ }; Packet::Packet (in_addr m_sip, unsigned short m_sport, in_addr m_dip, unsigned short m_dport, u_int32_t m_len, timeval m_time, direction m_dir) { sip = m_sip; sport = m_sport; dip = m_dip; dport = m_dport; len = m_len; time = m_time; dir = m_dir; sa_family = AF_INET; hashstring = NULL; } Packet::Packet (in6_addr m_sip, unsigned short m_sport, in6_addr m_dip, unsigned short m_dport, u_int32_t m_len, timeval m_time, direction m_dir) { sip6 = m_sip; sport = m_sport; dip6 = m_dip; dport = m_dport; len = m_len; time = m_time; dir = m_dir; sa_family = AF_INET6; hashstring = NULL; } Packet * Packet::newInverted () { /* TODO if this is a bottleneck, we can calculate the direction */ if (sa_family == AF_INET) return new Packet (dip, dport, sip, sport, len, time, dir_unknown); else return new Packet (dip6, dport, sip6, sport, len, time, dir_unknown); } /* constructs returns a new Packet() structure with the same contents as this one */ Packet::Packet (const Packet &old_packet) { sip = old_packet.sip; sport = old_packet.sport; sip6 = old_packet.sip6; dip6 = old_packet.dip6; dip = old_packet.dip; dport = old_packet.dport; len = old_packet.len; time = old_packet.time; sa_family = old_packet.sa_family; if (old_packet.hashstring == NULL) hashstring = NULL; else hashstring = strdup(old_packet.hashstring); dir = old_packet.dir; } bool sameinaddr(in_addr one, in_addr other) { return one.s_addr == other.s_addr; } bool Packet::isOlderThan (timeval t) { std::cout << "Comparing " << time.tv_sec << " <= " << t.tv_sec << std::endl; return (time.tv_sec <= t.tv_sec); } bool Packet::Outgoing () { /* must be initialised with getLocal("eth0:1");) */ assert (local_addrs != NULL); switch (dir) { case dir_outgoing: return true; case dir_incoming: return false; case dir_unknown: bool islocal; if (sa_family == AF_INET) islocal = local_addrs->contains(sip.s_addr); else islocal = local_addrs->contains(sip6); if (islocal) { dir = dir_outgoing; return true; } else { /*if (DEBUG) { if (sa_family == AF_INET) islocal = local_addrs->contains(dip.s_addr); else islocal = local_addrs->contains(dip6); if (!islocal) { std::cerr << "Neither dip nor sip are local: "; char addy [50]; inet_ntop (AF_INET6, &sip6, addy, 49); std::cerr << addy << std::endl; inet_ntop (AF_INET6, &dip6, addy, 49); std::cerr << addy << std::endl; return false; } }*/ dir = dir_incoming; return false; } } return false; } /* returns the packet in '1.2.3.4:5-1.2.3.4:5'-form, for use in the 'conninode' table */ /* '1.2.3.4' should be the local address. */ /* the calling code should take care of deletion of the hash string */ char * Packet::gethashstring () { if (hashstring != NULL) { return hashstring; } hashstring = (char *) malloc (HASHKEYSIZE * sizeof(char)); char * local_string = (char *) malloc (50); char * remote_string = (char *) malloc (50); if (sa_family == AF_INET) { inet_ntop(sa_family, &sip, local_string, 49); inet_ntop(sa_family, &dip, remote_string, 49); } else { inet_ntop(sa_family, &sip6, local_string, 49); inet_ntop(sa_family, &dip6, remote_string, 49); } if (Outgoing()) { snprintf(hashstring, HASHKEYSIZE * sizeof(char), "%s:%d-%s:%d", local_string, sport, remote_string, dport); } else { snprintf(hashstring, HASHKEYSIZE * sizeof(char), "%s:%d-%s:%d", remote_string, dport, local_string, sport); } free (local_string); free (remote_string); //if (DEBUG) // std::cout << "Returning newly created hash string: " << hashstring << std::endl; return hashstring; } /* 2 packets match if they have the same * source and destination ports and IP's. */ bool Packet::match (Packet * other) { return (sport == other->sport) && (dport == other->dport) && (sameinaddr(sip, other->sip)) && (sameinaddr(dip, other->dip)); } nethogs/packet.h0000644000175000017500000000261610371432303014416 0ustar arnoutenarnouten#ifndef __PACKET_H #define __PACKET_H #define _BSD_SOURCE 1 #include #include #include #include #include "nethogs.h" enum direction { dir_unknown, dir_incoming, dir_outgoing }; /* To initialise this module, call getLocal with the currently * monitored device (e.g. "eth0:1") */ void getLocal (const char *device, bool tracemode); class Packet { public: in6_addr sip6; in6_addr dip6; in_addr sip; in_addr dip; unsigned short sport; unsigned short dport; u_int32_t len; timeval time; Packet (in_addr m_sip, unsigned short m_sport, in_addr m_dip, unsigned short m_dport, u_int32_t m_len, timeval m_time, direction dir = dir_unknown); Packet (in6_addr m_sip, unsigned short m_sport, in6_addr m_dip, unsigned short m_dport, u_int32_t m_len, timeval m_time, direction dir = dir_unknown); /* copy constructor */ Packet (const Packet &old); ~Packet () { if (hashstring != NULL) { free (hashstring); hashstring = NULL; } } /* Packet (const Packet &old_packet); */ /* copy constructor that turns the packet around */ Packet * newInverted (); bool isOlderThan(timeval t); /* is this packet coming from the local host? */ bool Outgoing (); bool match (Packet * other); /* returns '1.2.3.4:5-1.2.3.4:6'-style string */ char * gethashstring(); private: direction dir; short int sa_family; char * hashstring; }; #endif nethogs/process.cpp0000644000175000017500000001772611607137020015171 0ustar arnoutenarnouten#include #include #include #include #include #include #include #include #include #include #include #include #include "process.h" #include "nethogs.h" /* #include "inodeproc.cpp" */ #include "inode2prog.h" #include "conninode.h" extern local_addr * local_addrs; /* * connection-inode table. takes information from /proc/net/tcp. * key contains source ip, source port, destination ip, destination * port in format: '1.2.3.4:5-1.2.3.4:5' */ extern std::map conninode; /* this file includes: * - calls to inodeproc to get the pid that belongs to that inode */ /* * Initialise the global process-list with some special processes: * * unknown TCP traffic * * UDP traffic * * unknown IP traffic * We must take care this one never gets removed from the list. */ Process * unknowntcp; Process * unknownudp; Process * unknownip; ProcList * processes; /* We're migrating to having several `unknown' processes that are added as * normal processes, instead of hard-wired unknown processes. * This mapping maps from unknown processes descriptions to processes */ std::map unknownprocs; void process_init () { unknowntcp = new Process (0, "", "unknown TCP"); //unknownudp = new Process (0, "", "unknown UDP"); //unknownip = new Process (0, "", "unknown IP"); processes = new ProcList (unknowntcp, NULL); //processes = new ProcList (unknownudp, processes); //processes = new ProcList (unknownip, processes); } int Process::getLastPacket() { int lastpacket=0; ConnList * curconn=connections; while (curconn != NULL) { assert (curconn != NULL); assert (curconn->getVal() != NULL); if (curconn->getVal()->getLastPacket() > lastpacket) lastpacket = curconn->getVal()->getLastPacket(); curconn = curconn->getNext(); } return lastpacket; } Process * findProcess (struct prg_node * node) { ProcList * current = processes; while (current != NULL) { Process * currentproc = current->getVal(); assert (currentproc != NULL); if (node->pid == currentproc->pid) return current->getVal(); current = current->next; } return NULL; } /* finds process based on inode, if any */ /* should be done quickly after arrival of the packet, * otherwise findPID will be outdated */ Process * findProcess (unsigned long inode) { struct prg_node * node = findPID(inode); if (node == NULL) return NULL; return findProcess (node); } /* check if we have identified any previously unknown * connections are now known * * When this is the case, something weird is going on. * This function is only called in bughunt-mode */ void reviewUnknown () { ConnList * curr_conn = unknowntcp->connections; ConnList * previous_conn = NULL; while (curr_conn != NULL) { unsigned long inode = conninode[curr_conn->getVal()->refpacket->gethashstring()]; if (inode != 0) { Process * proc = findProcess (inode); if (proc != unknowntcp && proc != NULL) { if (DEBUG || bughuntmode) std::cout << "FIXME: Previously unknown inode " << inode << " now got process - apparently it makes sense to review unknown connections\n"; /* Yay! - but how can this happen? */ assert(false); /* TODO: this needs some investigation/refactoring - we should never get here due to assert(false) */ if (previous_conn != NULL) { previous_conn->setNext (curr_conn->getNext()); proc->connections = new ConnList (curr_conn->getVal(), proc->connections); delete curr_conn; curr_conn = previous_conn; } else { unknowntcp->connections = curr_conn->getNext(); proc->connections = new ConnList (curr_conn->getVal(), proc->connections); delete curr_conn; curr_conn = unknowntcp->connections; } } } previous_conn = curr_conn; if (curr_conn != NULL) curr_conn = curr_conn->getNext(); } } int ProcList::size () { int i=1; if (next != NULL) i += next->size(); return i; } void check_all_procs () { ProcList * curproc = processes; while (curproc != NULL) { curproc->getVal()->check(); curproc = curproc->getNext(); } } /* * returns the process from proclist with matching pid * if the inode is not associated with any PID, return the unknown process * if the process is not yet in the proclist, add it */ Process * getProcess (unsigned long inode, const char * devicename) { struct prg_node * node = findPID(inode); if (node == NULL) { if (DEBUG || bughuntmode) std::cout << "No PID information for inode " << inode << std::endl; return unknowntcp; } Process * proc = findProcess (node); if (proc != NULL) return proc; Process * newproc = new Process (inode, devicename); newproc->name = strdup(node->name); newproc->pid = node->pid; char procdir [100]; sprintf(procdir , "/proc/%d", node->pid); struct stat stats; int retval = stat(procdir, &stats); /* 0 seems a proper default. * used in case the PID disappeared while nethogs was running * TODO we can store node->uid this while info on the inodes, * right? */ /* if (!ROBUST && (retval != 0)) { std::cerr << "Couldn't stat " << procdir << std::endl; assert (false); } */ if (retval != 0) newproc->setUid(0); else newproc->setUid(stats.st_uid); /*if (getpwuid(stats.st_uid) == NULL) { std::stderr << "uid for inode if (!ROBUST) assert(false); }*/ processes = new ProcList (newproc, processes); return newproc; } /* * Used when a new connection is encountered. Finds corresponding * process and adds the connection. If the connection doesn't belong * to any known process, the process list is updated and a new process * is made. If no process can be found even then, it's added to the * 'unknown' process. */ Process * getProcess (Connection * connection, const char * devicename) { unsigned long inode = conninode[connection->refpacket->gethashstring()]; if (inode == 0) { // no? refresh and check conn/inode table if (bughuntmode) { std::cout << "? new connection not in connection-to-inode table before refresh.\n"; } // refresh the inode->pid table first. Presumably processing the renewed connection->inode table // is slow, making this worthwhile. // We take the fact for granted that we might already know the inode->pid (unlikely anyway if we // haven't seen the connection->inode yet though). reread_mapping(); refreshconninode(); inode = conninode[connection->refpacket->gethashstring()]; if (bughuntmode) { if (inode == 0) { std::cout << ":( inode for connection not found after refresh.\n"; } else { std::cout << ":) inode for connection found after refresh.\n"; } } #if REVERSEHACK if (inode == 0) { /* HACK: the following is a hack for cases where the * 'local' addresses aren't properly recognised, as is * currently the case for IPv6 */ /* we reverse the direction of the stream if * successful. */ Packet * reversepacket = connection->refpacket->newInverted(); inode = conninode[reversepacket->gethashstring()]; if (inode == 0) { delete reversepacket; if (bughuntmode || DEBUG) std::cout << "LOC: " << connection->refpacket->gethashstring() << " STILL not in connection-to-inode table - adding to the unknown process\n"; unknowntcp->connections = new ConnList (connection, unknowntcp->connections); return unknowntcp; } delete connection->refpacket; connection->refpacket = reversepacket; } #endif } else if (bughuntmode) { std::cout << ";) new connection in connection-to-inode table before refresh.\n"; } if (bughuntmode) { std::cout << " inode # " << inode << std::endl; } Process * proc; if (inode == 0) { proc = new Process (0, "", connection->refpacket->gethashstring()); processes = new ProcList (proc, processes); } else { proc = getProcess(inode, devicename); } proc->connections = new ConnList (connection, proc->connections); return proc; } void procclean () { //delete conninode; prg_cache_clear(); } nethogs/process.h0000644000175000017500000000372011607137032014626 0ustar arnoutenarnouten#ifndef __PROCESS_H #define __PROCESS_H #include #include "nethogs.h" #include "connection.h" extern bool tracemode; extern bool bughuntmode; void check_all_procs (); class ConnList { public: ConnList (Connection * m_val, ConnList * m_next) { assert (m_val != NULL); val = m_val; next = m_next; } ~ConnList () { /* does not delete its value, to allow a connection to * remove itself from the global connlist in its destructor */ } Connection * getVal () { return val; } void setNext (ConnList * m_next) { next = m_next; } ConnList * getNext () { return next; } private: Connection * val; ConnList * next; }; class Process { public: /* the process makes a copy of the device name and name. */ Process (unsigned long m_inode, const char * m_devicename, const char * m_name = NULL) { //std::cout << "ARN: Process created with dev " << m_devicename << std::endl; if (DEBUG) std::cout << "PROC: Process created at " << this << std::endl; inode = m_inode; if (m_name == NULL) name = NULL; else name = strdup(m_name); devicename = strdup(m_devicename); connections = NULL; pid = 0; uid = 0; } void check () { assert (pid >= 0); } ~Process () { free (name); free (devicename); if (DEBUG) std::cout << "PROC: Process deleted at " << this << std::endl; } int getLastPacket (); char * name; char * devicename; int pid; unsigned long inode; ConnList * connections; uid_t getUid() { return uid; } void setUid(uid_t m_uid) { uid = m_uid; } private: uid_t uid; }; class ProcList { public: ProcList (Process * m_val, ProcList * m_next) { assert (m_val != NULL); val = m_val; next = m_next; } int size (); Process * getVal () { return val; } ProcList * getNext () { return next; } ProcList * next; private: Process * val; }; Process * getProcess (Connection * connection, const char * devicename = NULL); void process_init (); void refreshconninode (); void procclean (); #endif nethogs/README0000644000175000017500000000152210112336573013656 0ustar arnoutenarnouten= NETHOGS = http://nethogs.sf.net == INTRODUCTION == NetHogs is a small 'net top' tool. Instead of breaking the traffic down per protocol or per subnet, like most tools do, it groups bandwidth by process. NetHogs does not rely on a special kernel module to be loaded. If there's suddenly a lot of network traffic, you can fire up NetHogs and immediately see which PID is causing this. This makes it easy to indentify programs that have gone wild and are suddenly taking up your bandwidth. Since NetHogs heavily relies on /proc, it currently runs on Linux only. == STATUS == Ideas/ToDo for new releases: * Only IPv4 TCP is currently supported * Sort the output by other values than network usage * Monitor specific processes * Make it work correctly on machines with multiple IP addresses * Integrate into another tool?? == LICENSE == GPL. nethogs/README.decpcap.txt0000644000175000017500000000511310270737173016100 0ustar arnoutenarnoutenThis is a brainstorm about a libpcap-wrapper. It should make it possible to add callbacks requesting specific packets, for example asking for all TCP packets, whether they are sent over IPv4 or IPv6. Return value of the callback specifies of the packet should 'fall through', i.e., if it should be sent to other callbacks, too. give the programmer the opportunity to let packages re-enter the 'stream'. Callbacks should be called from high to low level. When a callback returns 'true', no lower callbacks should be called. The payload is available in a nice struct (union?), too. = Examples - how it'd work = == For the developers of the lib == When the sniffer is started, we learn what kind of packets are on the wire (ethernet, ppp, etc) and start pcap. Whenever a packet arrives, it is parsed. After parsing, if a callback is defined for this type of packet, the callback is pushed onto a stack. After that the payload is parsed. This goes on until the payload is, as far as we're concerned, raw data. Then the callbacks on the stack are called, until one of them returns 'true' ('done parsing this packet') Undefined callbacks move the parser to the next level. -- alternatively -- When the sniffer is started, we learn what kind of packets are on the wire (ethernet, ppp, etc) and start pcap. Whenever a packet arrives, it is parsed. After parsing, if a callback is defined for this type of packet, that callback is called. If it returns 'true', the packet is 'done', and discarded. If it returns 'false', it's passed on to the next level, leaving any changes to the user data intact. == For the users of the lib == If you want to sniff only tcp packets, add a callback for the 'packet_tcp' packet type. If you also want to count the total amount of IP traffic, make sure the 'packet_tcp' handler returns 'false' - that means after the tcp callback the packet will go on and be presented to the IP callback also. If you want to sniff specifically IPv4 TCP packets, you add a callback for IPv4 that calls the function to parse the payload directly, and then returns 'false'. If you modify the 'user' data in top-level callbacks which return 'false', -- alternatively -- If you want to sniff only tcp packets, simply only add a callback for 'dp_packet_tcp'. If, on top of that, you also want to count the total amount of IP traffic, make sure it returns 'false' and return. If you want to sniff specifically IPv4 TCP packets, you can do 2 things: add a 'true'-returning callback to everything else apart from IPv4 (which is ugly), or only add a callback for IPv4 and call the TCP-parsing code by hand. nethogs/refresh.cpp0000644000175000017500000000043710070267430015142 0ustar arnoutenarnouten#include #include #include #include "nethogs.h" extern bool needrefresh; extern unsigned refreshdelay; void alarm_cb (int i) { needrefresh = true; //cout << "Setting needrefresh\n"; signal (SIGALRM, &alarm_cb); alarm(refreshdelay); } nethogs/refresh.h0000644000175000017500000000002710070267430014602 0ustar arnoutenarnoutenvoid alarm_cb (int i);