commit be624683f0aa27c222b20657ed3030c1ade059e3 Author: Arnout Engelen Date: Tue Jun 29 13:31:04 2004 +0000 Initial import. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..fb540a3 --- /dev/null +++ b/INSTALL @@ -0,0 +1,7 @@ +make ; make install + +you need the 'libpcap-dev' and 'libpcap' packages. + +let me know if you have any other problems on nethogs@bzzt.net + + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..93c8acf --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +VERSION := 0 +SUBVERSION := 6 +MINORVERSION := pre + +bin := /usr/local/bin +man8 := /usr/local/man/man8/ + +all: nethogs + +CFLAGS=-g +OBJS=structs.o packet.o connection.o process.o hashtbl.o refresh.o +GCC=g++ + +.PHONY tgz + +tgz: clean + cd .. ; tar czvf nethogs-$(VERSION).$(SUBVERSION).$(MINORVERSION).tar.gz nethogs-$(VERSION).$(SUBVERSION)/* + +.PHONY check +check: + echo "Not implemented" + +install: nethogs nethogs.8 + cp nethogs $(bin) + cp nethogs.8 $(man8) + +nethogs: nethogs.cpp $(OBJS) + $(GCC) $(CFLAGS) nethogs.cpp $(OBJS) -o nethogs -lpcap -lncurses -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" + +#-lefence + +refresh.o: refresh.cpp refresh.h nethogs.h + $(GCC) $(CFLAGS) -c refresh.cpp +structs.o: structs.cpp structs.h nethogs.h + $(GCC) $(CFLAGS) -c structs.cpp +process.o: process.cpp process.h inodeproc.cpp nethogs.h + $(GCC) $(CFLAGS) -c process.cpp +packet.o: packet.cpp packet.h nethogs.h + $(GCC) $(CFLAGS) -c packet.cpp +connection.o: connection.cpp connection.h nethogs.h + $(GCC) $(CFLAGS) -c connection.cpp +hashtbl.o: hashtbl.cpp hashtbl.h nethogs.h + $(GCC) $(CFLAGS) -c hashtbl.cpp + +.PHONY clean +clean: + rm -f $(OBJS) + rm -f nethogs diff --git a/connection.cpp b/connection.cpp new file mode 100644 index 0000000..582938d --- /dev/null +++ b/connection.cpp @@ -0,0 +1,131 @@ +#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 (p); + return; + } + + if (content->val->time.tv_sec == p->time.tv_sec) + { + content->val->len += p->len; + return; + } + + content = new PackListNode(p, content); +} + +/* sums up the total bytes used and removes 'old' packets */ +bpf_u_int32 PackList::sumanddel (timeval t) +{ + bpf_u_int32 retval = 0; + PackListNode * current = content; + PackListNode * previous = NULL; + + while (current != NULL) + { + if (current->val->isOlderThan(t)) + { + 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; +} + +Connection::Connection (Packet * packet) +{ + if (DEBUG) + assert (packet != NULL); + connections = new ConnList (this, connections); + sent_packets = new PackList (); + recv_packets = new PackList (); + if (packet->Outgoing()) + { + sent_packets->add(packet); + } else { + recv_packets->add(packet); + } + refpacket = packet->newPacket (); + 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; + delete refpacket; + if (sent_packets != NULL) + delete sent_packets; + if (recv_packets != NULL) + delete recv_packets; +} + +void Connection::add (Packet * packet) +{ + lastpacket = packet->time.tv_sec; + if (packet->Outgoing()) + { + sent_packets->add (packet); + } else { + recv_packets->add (packet); + } +} + +Connection * findConnection (Packet * packet) +{ + ConnList * current = connections; + while (current != NULL) + { + if (packet->match(current->val->refpacket)) + return current->val; + + current = current->next; + } + 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, bpf_u_int32 * sent, bpf_u_int32 * recv) +{ + (*sent)=(*recv)=0; + + *sent = sent_packets->sumanddel(t); + *recv = recv_packets->sumanddel(t); +} diff --git a/connection.h b/connection.h new file mode 100644 index 0000000..2333ed4 --- /dev/null +++ b/connection.h @@ -0,0 +1,86 @@ +#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) + { + if (DEBUG) + 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 */ + bpf_u_int32 sumanddel (timeval t); + + 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 */ + Connection (Packet * packet); + + ~Connection(); + + /* add a packet to the packlist + * will delete the packet when it is + * 'merged' with another packet + */ + void add (Packet * packet); + + int getLastPacket () + { return lastpacket; } + + /* sums up the total bytes used + * and removes 'old' packets. */ + void sumanddel(timeval curtime, bpf_u_int32 * sent, bpf_u_int32 * recv); + + /* for checking if a packet is part of this connection */ + Packet * refpacket; +private: + PackList * sent_packets; + PackList * recv_packets; + int lastpacket; +}; + +/* Find the connection this packet belongs to */ +Connection * findConnection (Packet * packet); + +#endif diff --git a/hashtbl.cpp b/hashtbl.cpp new file mode 100644 index 0000000..a5222fc --- /dev/null +++ b/hashtbl.cpp @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include "hashtbl.h" + +HashNode::~HashNode () +{ + free (key); + //delete (content); + if (next) + delete (next); +} + +HashTable::HashTable(int n_size) +{ + // size = n_size; + // TODO allow for variable size + size = n_size; + table = (HashNode **) malloc (size * sizeof(HashNode *)); + for (unsigned int i=0; i> krightmove; + retval = retval << kleftmove; + retval ^= carry; + retval ^= str[i]; + } + return retval % size; +} + +HashNode * HashTable::newHashNode(char * key, void * content, HashNode * next) +{ + HashNode * retval = new HashNode (); + retval->key = key; + retval->content = content; + retval->next = next; + return retval; +} + + +void HashTable::add(char * key, void * content) +{ + unsigned int hkey = HashString (key); + //cout << "Adding node: " << key << " key " << hkey << endl; + table[hkey] = newHashNode(key, content, table[hkey]); +} + +void * HashTable::get(char * key) +{ + HashNode * current_node = table[HashString (key)]; + //cout << "looking for node " << HashString (key) << endl; + while (current_node != NULL) + { + //cout << "found node, key = " << current_node->key << endl; + if (strcmp(current_node->key, key) == 0) + { + return current_node->content; + } + current_node = current_node->next; + } + return NULL; +} diff --git a/hashtbl.h b/hashtbl.h new file mode 100644 index 0000000..a0ab12b --- /dev/null +++ b/hashtbl.h @@ -0,0 +1,30 @@ +#include +#include + +class HashNode +{ +public: + ~HashNode(); + + char * key; + void * content; + HashNode * next; +}; + +class HashTable +{ +public: + HashTable(int n_size); + ~HashTable(); + void add(char * key, void * content); + void * get(char * key); + +private: + int size; + HashNode ** table; + + HashNode * newHashNode(char * key, void * content, HashNode * next); + unsigned int HashString(const char * str); + + HashTable(); // We leave this unimplemented ;) +}; diff --git a/hashtest b/hashtest new file mode 100755 index 0000000..0a4a7fa Binary files /dev/null and b/hashtest differ diff --git a/hashtest.cpp b/hashtest.cpp new file mode 100644 index 0000000..7084a3b --- /dev/null +++ b/hashtest.cpp @@ -0,0 +1,11 @@ +#include +#include "hashtbl.h" + +void main () +{ + HashTable * table = new HashTable (10); + table->add("Foo", (void*)"Bar"); + table->add("Baz", (void*)"Qux"); + cout << "Foo is " << (char*)(table->get("Foo")) << endl; +} + diff --git a/inet6.c b/inet6.c new file mode 100644 index 0000000..306f65d --- /dev/null +++ b/inet6.c @@ -0,0 +1,169 @@ +/* + * lib/inet6.c This file contains an implementation of the "INET6" + * support functions for the net-tools. + * (most of it copied from lib/inet.c 1.26). + * + * Version: $Id$ + * + * Author: Fred N. van Kempen, + * Copyright 1993 MicroWalt Corporation + * + * Modified: + *960808 {0.01} Frank Strauss : adapted for IPv6 support + *980701 {0.02} Arnaldo C. Melo: GNU gettext instead of catgets + *990824 Bernd Eckenfels: clear members for selecting v6 address + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* +#include "version.h" +#include "net-support.h" +#include "pathnames.h" +#include "intl.h" +#include "util.h" +*/ + +extern int h_errno; /* some netdb.h versions don't export this */ + +static int INET6_resolve(char *name, struct sockaddr_in6 *sin6) +{ + struct addrinfo req, *ai; + int s; + + memset (&req, '\0', sizeof req); + req.ai_family = AF_INET6; + if ((s = getaddrinfo(name, NULL, &req, &ai))) { + fprintf(stderr, "getaddrinfo: %s: %d\n", name, s); + return -1; + } + memcpy(sin6, ai->ai_addr, sizeof(struct sockaddr_in6)); + + freeaddrinfo(ai); + + return (0); +} + +#ifndef IN6_IS_ADDR_UNSPECIFIED +#define IN6_IS_ADDR_UNSPECIFIED(a) \ + (((__u32 *) (a))[0] == 0 && ((__u32 *) (a))[1] == 0 && \ + ((__u32 *) (a))[2] == 0 && ((__u32 *) (a))[3] == 0) +#endif + + +static int INET6_rresolve(char *name, struct sockaddr_in6 *sin6, int numeric) +{ + int s; + + /* Grmpf. -FvK */ + if (sin6->sin6_family != AF_INET6) { +#ifdef DEBUG + fprintf(stderr, "rresolve: unsupport address family %d !\n", + sin6->sin6_family); +#endif + errno = EAFNOSUPPORT; + return (-1); + } + if (numeric & 0x7FFF) { + inet_ntop(AF_INET6, &sin6->sin6_addr, name, 80); + return (0); + } + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + if (numeric & 0x8000) + strcpy(name, "default"); + else + strcpy(name, "*"); + return (0); + } + + if ((s = getnameinfo((struct sockaddr *) sin6, sizeof(struct sockaddr_in6), + name, 255 /* !! */ , NULL, 0, 0))) { + fputs("getnameinfo failed\n", stderr); + return -1; + } + return (0); +} + + +static void INET6_reserror(char *text) +{ + herror(text); +} + + +/* Display an Internet socket address. */ +static char *INET6_print(unsigned char *ptr) +{ + static char name[80]; + + inet_ntop(AF_INET6, (struct in6_addr *) ptr, name, 80); + return name; +} + + +/* Display an Internet socket address. */ +/* dirty! struct sockaddr usually doesn't suffer for inet6 addresses, fst. */ +static char *INET6_sprint(struct sockaddr *sap, int numeric) +{ + static char buff[128]; + + if (sap->sa_family == 0xFFFF || sap->sa_family == 0) + return safe_strncpy(buff, "[NONE SET]", sizeof(buff)); + if (INET6_rresolve(buff, (struct sockaddr_in6 *) sap, numeric) != 0) + return safe_strncpy(buff, "[UNKNOWN]", sizeof(buff)); + return (buff); +} + + +static int INET6_getsock(char *bufp, struct sockaddr *sap) +{ + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *) sap; + sin6->sin6_family = AF_INET6; + sin6->sin6_port = 0; + + if (inet_pton(AF_INET6, bufp, sin6->sin6_addr.s6_addr) <= 0) + return (-1); + + return 16; /* ?;) */ +} + +static int INET6_input(int type, char *bufp, struct sockaddr *sap) +{ + switch (type) { + case 1: + return (INET6_getsock(bufp, sap)); + default: + return (INET6_resolve(bufp, (struct sockaddr_in6 *) sap)); + } +} + + +struct aftype inet6_aftype = +{ + "inet6", NULL, /*"IPv6", */ AF_INET6, sizeof(struct in6_addr), + INET6_print, INET6_sprint, INET6_input, INET6_reserror, + INET6_rprint, INET6_rinput, NULL, + + -1, + "/proc/net/if_inet6" +}; diff --git a/inodeproc.cpp b/inodeproc.cpp new file mode 100644 index 0000000..898494d --- /dev/null +++ b/inodeproc.cpp @@ -0,0 +1,226 @@ +/* this comes from netstat.c, but is very useful :)) */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct proginfo +{ + int pid; + char* name; +}; + +#define PATH_PROC "/proc" +#define PATH_FD_SUFF "fd" +#define PATH_PROC_X_FD PATH_PROC "/%s/" PATH_FD_SUFF +#define PATH_FD_SUFFl strlen(PATH_FD_SUFF) +#define PRG_SOCKET_PFX "socket:[" +#define PRG_SOCKET_PFXl (strlen(PRG_SOCKET_PFX)) +#define PRG_SOCKET_PFX2 "[0000]:" +#define PRG_SOCKET_PFX2l (strlen(PRG_SOCKET_PFX2)) +#define PATH_CMDLINE "cmdline" +#define PATH_CMDLINEl strlen(PATH_CMDLINE) +#define PRG_HASH_SIZE 211 +#define PRG_HASHIT(x) ((x) % PRG_HASH_SIZE) + +static struct prg_node { + struct prg_node *next; + int inode; + int pid; + char name[PROGNAME_WIDTH]; +} *prg_hash[PRG_HASH_SIZE]; + +static char prg_cache_loaded = 0; + +static void prg_cache_clear(void) +{ + struct prg_node **pnp,*pn; + + if (prg_cache_loaded == 2) + for (pnp=prg_hash;pnpnext; + free(pn); + } + prg_cache_loaded=0; +} + +static prg_node * prg_cache_get(int inode) +{ + unsigned hi=PRG_HASHIT(inode); + struct prg_node *pn; + + for (pn=prg_hash[hi];pn;pn=pn->next) + if (pn->inode==inode) return(pn); + + return(NULL); +} + +static void prg_cache_add(int inode, char *name, int pid) +{ + unsigned hi = PRG_HASHIT(inode); + struct prg_node **pnp,*pn; + + prg_cache_loaded=2; + for (pnp=prg_hash+hi;(pn=*pnp);pnp=&pn->next) { + if (pn->inode==inode) { + /* Some warning should be appropriate here + as we got multiple processes for one i-node */ + return; + } + } + if (!(*pnp=(prg_node*)malloc(sizeof(**pnp)))) + return; + pn=*pnp; + pn->next=NULL; + pn->inode=inode; + pn->pid=pid; + if (strlen(name)>sizeof(pn->name)-1) + name[sizeof(pn->name)-1]='\0'; + strcpy(pn->name,name); +} + +static void extract_type_1_socket_inode(const char lname[], long * inode_p) { + /* If lname is of the form "socket:[12345]", extract the "12345" + as *inode_p. Otherwise, return -1 as *inode_p. + */ + + if (strlen(lname) < PRG_SOCKET_PFXl+3) *inode_p = -1; + else if (memcmp(lname, PRG_SOCKET_PFX, PRG_SOCKET_PFXl)) *inode_p = -1; + else if (lname[strlen(lname)-1] != ']') *inode_p = -1; + else { + char inode_str[strlen(lname + 1)]; /* e.g. "12345" */ + const int inode_str_len = strlen(lname) - PRG_SOCKET_PFXl - 1; + char *serr; + + strncpy(inode_str, lname+PRG_SOCKET_PFXl, inode_str_len); + inode_str[inode_str_len] = '\0'; + *inode_p = strtol(inode_str,&serr,0); + if (!serr || *serr || *inode_p < 0 || *inode_p >= INT_MAX) + *inode_p = -1; + } +} + +static void extract_type_2_socket_inode(const char lname[], long * inode_p) { + /* If lname is of the form "[0000]:12345", extract the "12345" + as *inode_p. Otherwise, return -1 as *inode_p. + */ + + if (strlen(lname) < PRG_SOCKET_PFX2l+1) *inode_p = -1; + else if (memcmp(lname, PRG_SOCKET_PFX2, PRG_SOCKET_PFX2l)) *inode_p = -1; + else { + char *serr; + + *inode_p=strtol(lname + PRG_SOCKET_PFX2l,&serr,0); + if (!serr || *serr || *inode_p < 0 || *inode_p >= INT_MAX) + *inode_p = -1; + } +} + + +static void prg_cache_load(void) +{ + char line[LINE_MAX],eacces=0; + int procfdlen,fd,cmdllen,lnamelen; + char lname[30],cmdlbuf[512],finbuf[PROGNAME_WIDTH]; + long inode; + const char *cs,*cmdlp; + DIR *dirproc=NULL,*dirfd=NULL; + struct dirent *direproc,*direfd; + + if (prg_cache_loaded) return; + prg_cache_loaded=1; + cmdlbuf[sizeof(cmdlbuf)-1]='\0'; + if (!(dirproc=opendir(PATH_PROC))) goto fail; + while (errno=0,direproc=readdir(dirproc)) { +#ifdef DIRENT_HAVE_D_TYPE_WORKS + if (direproc->d_type!=DT_DIR) continue; +#endif + for (cs=direproc->d_name;*cs;cs++) + if (!isdigit(*cs)) + break; + if (*cs) + continue; + procfdlen=snprintf(line,sizeof(line),PATH_PROC_X_FD,direproc->d_name); + if (procfdlen<=0 || procfdlen>=sizeof(line)-5) + continue; + errno=0; + dirfd=opendir(line); + if (! dirfd) { + if (errno==EACCES) + eacces=1; + continue; + } + line[procfdlen] = '/'; + cmdlp = NULL; + while ((direfd = readdir(dirfd))) { +#ifdef DIRENT_HAVE_D_TYPE_WORKS + if (direfd->d_type!=DT_LNK) + continue; +#endif + if (procfdlen+1+strlen(direfd->d_name)+1>sizeof(line)) + continue; + memcpy(line + procfdlen - PATH_FD_SUFFl, PATH_FD_SUFF "/", + PATH_FD_SUFFl+1); + strcpy(line + procfdlen + 1, direfd->d_name); + lnamelen=readlink(line,lname,sizeof(lname)-1); + lname[lnamelen] = '\0'; /*make it a null-terminated string*/ + + extract_type_1_socket_inode(lname, &inode); + + if (inode < 0) extract_type_2_socket_inode(lname, &inode); + + if (inode < 0) continue; + + if (!cmdlp) { + if (procfdlen - PATH_FD_SUFFl + PATH_CMDLINEl >= + sizeof(line) - 5) + continue; + strcpy(line + procfdlen-PATH_FD_SUFFl, PATH_CMDLINE); + fd = open(line, O_RDONLY); + if (fd < 0) + continue; + cmdllen = read(fd, cmdlbuf, sizeof(cmdlbuf) - 1); + if (close(fd)) + continue; + if (cmdllen == -1) + continue; + if (cmdllen < sizeof(cmdlbuf) - 1) + cmdlbuf[cmdllen]='\0'; + if ((cmdlp = strrchr(cmdlbuf, '/'))) + cmdlp++; + else + cmdlp = cmdlbuf; + } + + //snprintf(finbuf, sizeof(finbuf), "%s/%s", direproc->d_name, cmdlp); + snprintf(finbuf, sizeof(finbuf), "%s", cmdlp); + int pid; + sscanf(direproc->d_name, "%d", &pid); + prg_cache_add(inode, finbuf, pid); + } + closedir(dirfd); + dirfd = NULL; + } + if (dirproc) + closedir(dirproc); + if (dirfd) + closedir(dirfd); + if (!eacces) + return; + if (prg_cache_loaded == 1) { + fail: + fprintf(stderr,"(No info could be read for \"-p\": geteuid()=%d but you should be root.)\n", + geteuid()); + } + else + fprintf(stderr, "(Not all processes could be identified, non-owned process info\n" + " will not be shown, you would have to be root to see it all.)\n"); +} + diff --git a/nethogs.8 b/nethogs.8 new file mode 100644 index 0000000..66a9eb5 --- /dev/null +++ b/nethogs.8 @@ -0,0 +1,27 @@ +.\" This page Copyright (C) 2004 Fabian Frederick +.\" Content based on Nethogs homepage by Arnout Engelen +.TH NETHOGS 8 "14 February 2004" +.SH NAME +nethogs +.SH SYNOPSIS +.ft B +.B nethogs +.RB [ "\-h" ] +.RB [ "\-d" ] +.RI [device ] +.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 +The \fB-h\fP switch display available commands usage +.PP +The \fB-d\fP delay for refresh rate +.PP +.I device +by default eth0 is being used + +.SH "SEE ALSO" +.I netstat(8) tcpdump(1) pcap(3) +.SH AUTHOR +.nf +Written by Arnout Engelen . diff --git a/nethogs.cpp b/nethogs.cpp new file mode 100644 index 0000000..1e68c5f --- /dev/null +++ b/nethogs.cpp @@ -0,0 +1,175 @@ +/* nethogs.cpp + * + * Updates: + * + * 14/02/04 (Fabian) + * -Refresh delay + * -Help + * -Handling command-line options + * + * 06/04/04 (Fabian) + * -getLocal + * -I/O by balance + * -forceExit + * + * 10/05/04 (Arnout) + * -cleanups + * -splitting out incoming and outgoing traffic + * (based on 'I/O by balance' by Fabian) + */ + +#include "nethogs.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +extern "C" { + #include +} + +#include "packet.h" +#include "connection.h" +#include "process.h" +#include "refresh.h" + +bool needrefresh = false; +unsigned refreshdelay = 1; + +const char version[] = " version " VERSION "." SUBVERSION "." MINORVERSION; + +timeval curtime; +std::string * caption; + + + +void process (u_char * args, const struct pcap_pkthdr * header, const u_char * m_packet) +{ + curtime = header->ts; + + Packet * packet = getPacket (header, m_packet); + if (packet == NULL) + return; + + Connection * connection = findConnection(packet); + if (connection != NULL) + { + connection->add(packet); + return; + } + connection = new Connection (packet); + Process * process = getProcess(connection); + //process->addConnection (connection); + + if (needrefresh) + { + do_refresh(); + needrefresh = false; + } + + return; +} + +void quit_cb (int i) +{ + procclean(); + clear(); + endwin(); + exit(0); +} + +void forceExit(const char *msg) +{ + clear(); + endwin(); + std::cerr << msg << std::endl; + exit(0); +} + +static void versiondisplay(void) +{ + + std::cerr << version << "\n"; +} + +static void help(void) +{ + std::cerr << "usage: nethogs [-V] [-d] [device]\n"; + std::cerr << " -V : prints version.\n"; + std::cerr << " -d : delay for update refresh rate in seconds. default is 1.\n"; + std::cerr << " device : device to monitor. default is eth0\n"; +} + +int main (int argc, char** argv) +{ + char* dev = strdup("eth0"); + + for (argv++; *argv; argv++) + { + if (**argv=='-') + { + (*argv)++; + switch(**argv) + { + case 'V': versiondisplay(); + exit(0); + case 'h': help(); + exit(0); + case 'd': if (argv[1]) + { + argv++; + refreshdelay=atoi(*argv); + } + break; + default : help(); + exit(0); + } + } + else + { + dev = strdup(*argv); + } + } +#if DEBUG +#else + initscr(); + raw(); + noecho(); + cbreak(); +#endif + getLocal(dev); + + caption = new std::string ("NetHogs"); + caption->append(version); + caption->append(", running at "); + caption->append(dev); + + if (NEEDROOT && (getuid() != 0)) + forceExit("You need to be root to run NetHogs !"); + + char errbuf[PCAP_ERRBUF_SIZE]; + + pcap_t * handle; + handle = pcap_open_live(dev, BUFSIZ, 0, 1000, errbuf); + + signal (SIGALRM, &alarm_cb); + signal (SIGINT, &quit_cb); + alarm (refreshdelay); + while (1) + { + pcap_dispatch (handle, -1, process, NULL); + if (needrefresh) + { + do_refresh(); + needrefresh = false; + } + } +} + diff --git a/nethogs.h b/nethogs.h new file mode 100644 index 0000000..c955891 --- /dev/null +++ b/nethogs.h @@ -0,0 +1,28 @@ +#ifndef __NETHOGS_H +#define __NETHOGS_H + + +#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 + +/* 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 PROGNAME_WIDTH 30 + +void forceExit(const char *msg); + +#endif diff --git a/packet.cpp b/packet.cpp new file mode 100644 index 0000000..98e86f6 --- /dev/null +++ b/packet.cpp @@ -0,0 +1,178 @@ +#include +#include "packet.h" +#include +#include +#include +#include +#include +#include +#include "nethogs.h" +// #include "inet6.c" + +struct in_addr * local_addr = NULL; + +/* + * getLocal + * device: This should be device explicit (e.g. eth0:1) + * + * ioctl device for address + */ +void getLocal (const char *device) +{ + int sock; + struct ifreq iFreq; + struct sockaddr_in *saddr; + + if (local_addr != NULL) + { + std::cerr << "getLocal only needs to be called once in connection.cpp" << std::endl; + free (local_addr); + } + + local_addr = (struct in_addr *) malloc (sizeof(struct in_addr)); + + if((sock=socket(AF_INET, SOCK_PACKET, 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"); + } + saddr=(struct sockaddr_in*)&iFreq.ifr_addr; + (*local_addr)=saddr->sin_addr; +} + +typedef u_int32_t tcp_seq; + +/* ethernet header */ +struct ethernet_hdr { + u_char ether_dhost[ETHER_ADDR_LEN]; + u_char ether_shost[ETHER_ADDR_LEN]; + u_short ether_type; /* IP? */ +}; + +/* IP header */ +struct ip_hdr +{ +#if BYTE_ORDER == LITTLE_ENDIAN + u_int ip_hl:4, /* header length */ + ip_v:4; /* version */ +#if BYTE_ORDER == BIG_ENDIAN + u_int ip_v:4, /* version */ + ip_hl:4; /* header length */ +#endif +#endif /* not _IP_VHL */ + u_char ip_tos; /* type of service */ + u_short ip_len; /* total length */ + u_short ip_id; /* identification */ + u_short ip_off; /* fragment offset field */ +#define IP_RF 0x8000 /* reserved fragment flag */ +#define IP_DF 0x4000 /* dont fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + u_char ip_ttl; /* time to live */ + u_char ip_p; /* protocol */ + u_short ip_sum; /* checksum */ + struct in_addr ip_src,ip_dst; /* source and dest address */ +}; + +/* TCP header */ +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 'Constructor' - but returns NULL on failure */ +Packet * getPacket (const struct pcap_pkthdr * header, const u_char * packet) +{ + const struct ethernet_hdr * ethernet = (struct ethernet_hdr *)packet; + if (ethernet->ether_type != 8) + { +#if DEBUG + cerr << "Dropped non-ip packet of type " << ethernet->ether_type << endl; +#endif + return NULL; + } + + const struct ip_hdr * ip = (struct ip_hdr *)(packet + sizeof(ethernet_hdr)); + if (ip->ip_p != 6) + { +#if DEBUG + cerr << "Dropped non-tcp packet of type " << (int)(ip->ip_p) << endl; +#endif + return NULL; + } + + const struct tcp_hdr * tcp = (struct tcp_hdr *)(packet + sizeof(ethernet_hdr) + sizeof(ip_hdr)); + + return new Packet (ip->ip_src, ntohs(tcp->th_sport), ip->ip_dst, ntohs(tcp->th_dport), header->len, header->ts); +} + +Packet::Packet (in_addr m_sip, unsigned short m_sport, in_addr m_dip, unsigned short m_dport, bpf_u_int32 m_len, timeval m_time) +{ + sip = m_sip; sport = m_sport; + dip = m_dip; dport = m_dport; + len = m_len; time = m_time; +} + +Packet * Packet::newPacket () +{ + return new Packet (sip, sport, dip, dport, len, time); +} + +bool sameinaddr(in_addr one, in_addr other) +{ + return one.s_addr == other.s_addr; +} + +bool Packet::isOlderThan (timeval t) { + return (time.tv_sec + PERIOD <= t.tv_sec); +} + +bool Packet::Outgoing () { + /* must be initialised with getLocal("eth0:1");) */ + if (DEBUG) + assert (local_addr != NULL); + + return (sip.s_addr == local_addr->s_addr); +} + +char * Packet::gethashstring () +{ + // TODO this needs to be bigger to support ipv6?! + char * retval = (char *) malloc (92 * sizeof(char)); + snprintf(retval, 92 * sizeof(char), "%s:%d-", inet_ntoa(sip), sport); + snprintf(retval, 92 * sizeof(char), "%s%s:%d", retval, inet_ntoa(dip), dport); + //if (DEBUG) + //cout << "hasshtring: " << retval << endl; + return retval; +} + +bool Packet::match (Packet * other) +{ + return ((sport == other->sport) && (dport == other->dport) + && (sameinaddr(sip, other->sip)) && (sameinaddr(dip, other->dip))); +} diff --git a/packet.h b/packet.h new file mode 100644 index 0000000..eb97e16 --- /dev/null +++ b/packet.h @@ -0,0 +1,45 @@ +#ifndef __PACKET_H +#define __PACKET_H + +#define _BSD_SOURCE 1 +#include + +#include +#include +#include + +extern "C" +{ + #include +} + +/* To initialise this module, call getLocal with the currently + * monitored device (e.g. "eth0:1") */ +void getLocal (const char *device); + +class Packet +{ +public: + in_addr sip; + in_addr dip; + unsigned short sport; + unsigned short dport; + bpf_u_int32 len; + timeval time; + + Packet (in_addr m_sip, unsigned short m_sport, in_addr m_dip, unsigned short m_dport, bpf_u_int32 m_len, timeval m_time); + /* copy constructor */ + Packet * newPacket (); + + bool isOlderThan(timeval t); + /* is this packet coming from here? */ + bool Outgoing (); + + bool match (Packet * other); + /* returns '1.2.3.4:5-1.2.3.4:6'-style string */ + char * gethashstring(); +}; + +Packet * getPacket (const struct pcap_pkthdr * header, const u_char * packet); + +#endif diff --git a/process.cpp b/process.cpp new file mode 100644 index 0000000..c987f3f --- /dev/null +++ b/process.cpp @@ -0,0 +1,463 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "process.h" +#include "hashtbl.h" +#include "nethogs.h" +#include "inodeproc.cpp" +//#include "inet6.c" + +extern timeval curtime; +extern std::string * caption; + +/*struct aftype inet6_aftype = +{ + "inet6", NULL, AF_INET6, sizeof(struct in6_addr), + INET6_print, INET6_sprint, INET6_input, INET6_reserror, + INET6_rprint, INET6_rinput, NULL, + + -1, + "/proc/net/if_inet6" +};*/ + + +static int INET6_getsock(char *bufp, struct sockaddr *sap) +{ + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *) sap; + sin6->sin6_family = AF_INET6; + sin6->sin6_port = 0; + + if (inet_pton(AF_INET6, bufp, sin6->sin6_addr.s6_addr) <= 0) + return (-1); + + return 16; /* ?;) */ +} + +static int INET6_input(int type, char *bufp, struct sockaddr *sap) +{ + return (INET6_getsock(bufp, sap)); +} + +struct aftype { + char *name; + char *title; + int af; + int alen; + char *(*print) (unsigned char *); + char *(*sprint) (struct sockaddr *, int numeric); + int (*input) (int type, char *bufp, struct sockaddr *); + void (*herror) (char *text); + int (*rprint) (int options); + int (*rinput) (int typ, int ext, char **argv); + + /* may modify src */ + int (*getmask) (char *src, struct sockaddr * mask, char *name); + + int fd; + char *flag_file; +}; +/* + * 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' + */ +HashTable * conninode = new HashTable (256); + +// TODO check what happens to the 'content' field of the hash!! +void addtoconninode (char * buffer) +{ + char rem_addr[128], local_addr[128]; + int local_port, rem_port; + struct sockaddr_in6 localaddr, remaddr; + char addr6[INET6_ADDRSTRLEN]; + struct in6_addr in6; + extern struct aftype inet6_aftype; + // the following line leaks memory. + unsigned long * inode = (unsigned long *) malloc (sizeof(unsigned long)); + // TODO check it matched + sscanf(buffer, "%*d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %*X %*lX:%*lX %*X:%*lX %*lX %*d %*d %ld %*512s\n", + local_addr, &local_port, rem_addr, &rem_port, inode); + + if (strlen(local_addr) > 8) + { + /* Demangle what the kernel gives us */ + sscanf(local_addr, "%08X%08X%08X%08X", + &in6.s6_addr32[0], &in6.s6_addr32[1], + &in6.s6_addr32[2], &in6.s6_addr32[3]); + inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6)); + INET6_getsock(addr6, (struct sockaddr *) &localaddr); + sscanf(rem_addr, "%08X%08X%08X%08X", + &in6.s6_addr32[0], &in6.s6_addr32[1], + &in6.s6_addr32[2], &in6.s6_addr32[3]); + inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6)); + INET6_getsock(addr6, (struct sockaddr *) &remaddr); + localaddr.sin6_family = AF_INET6; + remaddr.sin6_family = AF_INET6; + } + else + { + sscanf(local_addr, "%X", &((struct sockaddr_in *)&localaddr)->sin_addr.s_addr); + sscanf(rem_addr, "%X", &((struct sockaddr_in *)&remaddr)->sin_addr.s_addr); + ((struct sockaddr *) &localaddr)->sa_family = AF_INET; + ((struct sockaddr *) &remaddr)->sa_family = AF_INET; + } + + char * hashkey = (char *) malloc (92 * sizeof(char)); + snprintf(hashkey, 92 * sizeof(char), "%s:%d-", inet_ntoa(((struct sockaddr_in *)&localaddr)->sin_addr), local_port); + snprintf(hashkey, 92 * sizeof(char), "%s%s:%d", hashkey, inet_ntoa(((struct sockaddr_in *)&remaddr)->sin_addr), rem_port); + conninode->add(hashkey, (void *)inode); + + // also add the reverse. + hashkey = (char *) malloc (92 * sizeof(char)); + snprintf(hashkey, 92 * sizeof(char), "%s:%d-", inet_ntoa(((struct sockaddr_in *)&remaddr)->sin_addr), rem_port); + snprintf(hashkey, 92 * sizeof(char), "%s%s:%d", hashkey, inet_ntoa(((struct sockaddr_in *)&localaddr)->sin_addr), local_port); + conninode->add(hashkey, (void *)inode); + + // also add the aliases :S + if (strcmp(inet_ntoa(((struct sockaddr_in *)&localaddr)->sin_addr), "172.16.3.1") == 0) + { + hashkey = (char *) malloc (92 * sizeof(char)); + snprintf(hashkey, 92 * sizeof(char), "%s:%d-", "195.169.216.157", local_port); + snprintf(hashkey, 92 * sizeof(char), "%s%s:%d", hashkey, inet_ntoa(((struct sockaddr_in *)&remaddr)->sin_addr), rem_port); + conninode->add(hashkey, (void *)inode); + + hashkey = (char *) malloc (92 * sizeof(char)); + snprintf(hashkey, 92 * sizeof(char), "%s:%d-", inet_ntoa(((struct sockaddr_in *)&remaddr)->sin_addr), rem_port); + snprintf(hashkey, 92 * sizeof(char), "%s%s:%d", hashkey, "195.169.216.157", local_port); + conninode->add(hashkey, (void *)inode); + } + if (strcmp(inet_ntoa(((struct sockaddr_in *)&remaddr)->sin_addr), "172.16.3.1") == 0) + { + hashkey = (char *) malloc (92 * sizeof(char)); + snprintf(hashkey, 92 * sizeof(char), "%s:%d-", inet_ntoa(((struct sockaddr_in *)&localaddr)->sin_addr), local_port); + snprintf(hashkey, 92 * sizeof(char), "%s%s:%d", hashkey, "195.169.216.157", rem_port); + conninode->add(hashkey, (void *)inode); + + hashkey = (char *) malloc (92 * sizeof(char)); + snprintf(hashkey, 92 * sizeof(char), "%s:%d-", "195.169.216.157", rem_port); + snprintf(hashkey, 92 * sizeof(char), "%s%s:%d", hashkey, inet_ntoa(((struct sockaddr_in *)&localaddr)->sin_addr), local_port); + conninode->add(hashkey, (void *)inode); + } + if (strcmp(inet_ntoa(((struct sockaddr_in *)&localaddr)->sin_addr), "195.169.216.157") == 0) + { + hashkey = (char *) malloc (92 * sizeof(char)); + snprintf(hashkey, 92 * sizeof(char), "%s:%d-", "172.16.3.1", local_port); + snprintf(hashkey, 92 * sizeof(char), "%s%s:%d", hashkey, inet_ntoa(((struct sockaddr_in *)&remaddr)->sin_addr), rem_port); + conninode->add(hashkey, (void *)inode); + + hashkey = (char *) malloc (92 * sizeof(char)); + snprintf(hashkey, 92 * sizeof(char), "%s:%d-", inet_ntoa(((struct sockaddr_in *)&remaddr)->sin_addr), rem_port); + snprintf(hashkey, 92 * sizeof(char), "%s%s:%d", hashkey, "172.16.3.1", local_port); + conninode->add(hashkey, (void *)inode); + } + if (strcmp(inet_ntoa(((struct sockaddr_in *)&remaddr)->sin_addr), "195.169.216.157") == 0) + { + hashkey = (char *) malloc (92 * sizeof(char)); + snprintf(hashkey, 92 * sizeof(char), "%s:%d-", inet_ntoa(((struct sockaddr_in *)&localaddr)->sin_addr), local_port); + snprintf(hashkey, 92 * sizeof(char), "%s%s:%d", hashkey, "172.16.3.1", rem_port); + conninode->add(hashkey, (void *)inode); + + hashkey = (char *) malloc (92 * sizeof(char)); + snprintf(hashkey, 92 * sizeof(char), "%s:%d-", "172.16.3.1", rem_port); + snprintf(hashkey, 92 * sizeof(char), "%s%s:%d", hashkey, inet_ntoa(((struct sockaddr_in *)&localaddr)->sin_addr), local_port); + conninode->add(hashkey, (void *)inode); + } +} + +void refreshconninode () +{ + delete conninode; + conninode = new HashTable (256); + + char buffer[8192]; + FILE * procinfo = fopen ("/proc/net/tcp", "r"); + if (procinfo) + { + fgets(buffer, sizeof(buffer), procinfo); + do + { + if (fgets(buffer, sizeof(buffer), procinfo)) + addtoconninode(buffer); + } while (!feof(procinfo)); + fclose(procinfo); + } + else + { + std::cout << "Error: couldn't open /proc/net/tcp\n"; + exit(0); + } + + procinfo = fopen ("/proc/net/tcp6", "r"); + if (procinfo != NULL) { + fgets(buffer, sizeof(buffer), procinfo); + do { + if (fgets(buffer, sizeof(buffer), procinfo)) + addtoconninode(buffer); + } while (!feof(procinfo)); + fclose (procinfo); + } +} + +class ProcList +{ +public: + ProcList (Process * m_val, ProcList * m_next) + { + if (DEBUG) + assert (m_val != NULL); + val = m_val; next = m_next; + } + Process * getVal () { return val; } + ProcList * getNext () { return next; } + ProcList * next; +private: + Process * val; +}; + +Process * unknownproc = new Process (0, "unknown"); +ProcList * processes = new ProcList (unknownproc, NULL); + +float tokbps (bpf_u_int32 bytes) +{ + return (((double)bytes) / PERIOD) / 1024; +} + +char * uid2username (int uid) +{ + struct passwd * pwd; + /* getpwuid() allocates space for this itself, + * which we shouldn't free */ + pwd = getpwuid(uid); + if (pwd == NULL) + { + return strdup ("unlisted"); + } else { + return strdup(pwd->pw_name); + } +} + + +class Line +{ +public: + Line (const char * name, double n_sent_kbps, double n_recv_kbps, int pid, int uid) + { + m_name = name; + sent_kbps = n_sent_kbps; + recv_kbps = n_recv_kbps; + m_pid = pid; + m_uid = uid; + } + + void show (int row) + { +#if DEBUG + std::cout << m_name << "\t" << m_sent_kbps << "\t" << recv_kbps << std::endl; +#else + mvprintw (3+row, 0, "%d", m_pid); + char * username = uid2username(m_uid); + mvprintw (3+row, 6, "%s", username); + free (username); + mvprintw (3+row, 6 + 9, "%s", m_name); + mvprintw (3+row, 6 + 9 + PROGNAME_WIDTH + 2, "%10.3f", sent_kbps); + mvprintw (3+row, 6 + 9 + PROGNAME_WIDTH + 2 + 9 + 3, "%10.3f", recv_kbps); + mvprintw (3+row, 6 + 9 + PROGNAME_WIDTH + 2 + 9 + 3 + 11, "KB/sec", recv_kbps); + // TODO fix + //if(m_kbps-upload_kbps>upload_kbps) + // mvprintw (3+row, 6 + 20 + PROGNAME_WIDTH + 2, "<<<<"); + // else mvprintw (3+row, 6 + 20 + PROGNAME_WIDTH + 2, ">>>>"); + +#endif + } + + double sent_kbps; + double recv_kbps; +private: + const char * m_name; + int m_pid; + int m_uid; +}; + +int GreatestFirst (const void * ma, const void * mb) +{ + Line ** pa = (Line **)ma; + Line ** pb = (Line **)mb; + Line * a = *pa; + Line * b = *pb; + if (a->recv_kbps > b->recv_kbps) + { + return -1; + } + if (a->recv_kbps == b->recv_kbps) + { + return 0; + } + return 1; +} +int count_processes() +{ + int i = 0; + ProcList * curproc = processes; + while (curproc != NULL) + { + i++; curproc = curproc->getNext(); + } + return i; +} + +void do_refresh() +{ + if (!DEBUG) + { + clear(); + mvprintw (0, 0, "%s", caption->c_str()); + attron(A_REVERSE); + mvprintw (2, 0, " PID USER PROGRAM SENT RECEIVED "); + attroff(A_REVERSE); + } + else + std::cout << "Refreshing:\n"; + ProcList * curproc = processes; + ProcList * lastproc = NULL; + int nproc = count_processes(); + Line * lines [nproc]; + int n = 0; + while (curproc != NULL) + { + // walk though its connections, summing up + // their data, and throwing away old stuff. + // if the last packet is older than PROCESSTIMEOUT seconds, discard. + if (DEBUG) + { + assert (curproc != NULL); + assert (curproc->getVal() != NULL); + } + if (curproc->getVal()->getLastPacket() + PROCESSTIMEOUT <= curtime.tv_sec) + { + if (lastproc) + { + lastproc->next = curproc->next; + ProcList * newcur = curproc->next; + delete curproc; + curproc = newcur; + nproc--; + } else { + processes = curproc->getNext(); + delete curproc; + curproc = processes; + nproc--; + } + } + else + { + bpf_u_int32 sum = 0, + sum_local = 0, + sum_conn = 0, + sum_connLocal = 0; + ConnList * curconn = curproc->getVal()->incoming; + while (curconn != NULL) + { + curconn->getVal()->sumanddel(curtime, &sum, &sum_local); + sum_connLocal+=sum_local; + sum_conn+=sum; + curconn = curconn->getNext(); + } + lines[n] = new Line (curproc->getVal()->name, tokbps(sum_conn), tokbps(sum_connLocal), curproc->getVal()->pid, curproc->getVal()->uid); + lastproc = curproc; + curproc = curproc->next; + n++; + } + } + qsort (lines, nproc, sizeof(Line *), GreatestFirst); + for (int i=0; ishow(i); + delete lines[i]; + } + if (!DEBUG) + refresh(); +} + +/* returns the process from proclist with matching pid + * if none, creates it */ +Process * getProcess (unsigned long inode) +{ + struct prg_node * node = prg_cache_get(inode); + + if (node == NULL) + { + prg_cache_clear(); + prg_cache_load(); + node = prg_cache_get(inode); + if (node == NULL) + return unknownproc; + } + + ProcList * current = processes; + while (current != NULL) + { + if (node->pid == current->getVal()->pid) + return current->getVal(); + current = current->next; + } + + Process * newproc = new Process (inode); + newproc->name = strdup(node->name); + newproc->pid = node->pid; + + char procdir [100]; + sprintf(procdir , "/proc/%d", node->pid); + struct stat stats; + stat(procdir, &stats); + newproc->uid = stats.st_uid; + + processes = new ProcList (newproc, processes); + return newproc; +} + +Process * getProcess (Connection * connection) +{ + ProcList * curproc = processes; + + // see if we already know the inode for this connection + if (DEBUG) + std::cout << "Connection reference packet found at " << connection->refpacket << std::endl; + unsigned long * inode = (unsigned long *) conninode->get(connection->refpacket->gethashstring()); + + if (inode == NULL) + { + // no? refresh and check conn/inode table +#if DEBUG + cerr << "Not in table, refreshing it.\n"; +#endif + refreshconninode(); + inode = (unsigned long *) conninode->get(connection->refpacket->gethashstring()); + if (inode == NULL) + { +#if DEBUG + //cerr << connection->refpacket->gethashstring() << " STILL not in table - dropping\n"; +#endif + return NULL; + } + } + + Process * proc = getProcess(*inode); + proc->incoming = new ConnList (connection, proc->incoming); + return proc; +} + +void procclean () +{ + delete conninode; + prg_cache_clear(); +} diff --git a/process.h b/process.h new file mode 100644 index 0000000..1922110 --- /dev/null +++ b/process.h @@ -0,0 +1,72 @@ +#ifndef __PROCESS_H +#define __PROCESS_H + +#include +#include "nethogs.h" +#include "connection.h" + +class ConnList +{ +public: + ConnList (Connection * m_val, ConnList * m_next) + { + if (DEBUG) + assert (m_val != NULL); + val = m_val; next = m_next; + } + Connection * getVal () + { + return val; + } + ConnList * getNext () + { + return next; + } +private: + Connection * val; + ConnList * next; +}; + +class Process +{ +public: + Process (unsigned long m_inode, char* m_name = NULL) + { + inode = m_inode; + name = m_name; + incoming = NULL; + outgoing = NULL; + } + int getLastPacket () + { + int lastpacket=0; + ConnList * curconn=incoming; + while (curconn != NULL) + { + if (DEBUG) + { + assert (curconn != NULL); + assert (curconn->getVal() != NULL); + } + if (curconn->getVal()->getLastPacket() > lastpacket) + lastpacket = curconn->getVal()->getLastPacket(); + curconn = curconn->getNext(); + } + return lastpacket; + } + + const char * name; + int pid; + int uid; + + unsigned long inode; + ConnList * incoming; + ConnList * outgoing; +}; + +Process * getProcess (Connection * connection); +void do_refresh (); + +void procclean (); + +#endif diff --git a/refresh.cpp b/refresh.cpp new file mode 100644 index 0000000..79adbf5 --- /dev/null +++ b/refresh.cpp @@ -0,0 +1,17 @@ +#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); +} + diff --git a/refresh.h b/refresh.h new file mode 100644 index 0000000..0a5bf88 --- /dev/null +++ b/refresh.h @@ -0,0 +1 @@ +void alarm_cb (int i); diff --git a/structs.cpp b/structs.cpp new file mode 100644 index 0000000..e69de29 diff --git a/structs.h b/structs.h new file mode 100644 index 0000000..e69de29