Initial import.
This commit is contained in:
7
INSTALL
Normal file
7
INSTALL
Normal file
@@ -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
|
||||
|
||||
|
||||
48
Makefile
Normal file
48
Makefile
Normal file
@@ -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
|
||||
131
connection.cpp
Normal file
131
connection.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
#include <iostream>
|
||||
#include <assert.h>
|
||||
#include <malloc.h>
|
||||
#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);
|
||||
}
|
||||
86
connection.h
Normal file
86
connection.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifndef __CONNECTION_H
|
||||
#define __CONNECTION_H
|
||||
|
||||
#include <iostream>
|
||||
#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
|
||||
90
hashtbl.cpp
Normal file
90
hashtbl.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include <iostream>
|
||||
#include <values.h>
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#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<size; i++)
|
||||
{
|
||||
table[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
HashTable::~HashTable()
|
||||
{
|
||||
for (unsigned int i=0; i<size; i++)
|
||||
{
|
||||
if (table[i])
|
||||
delete table[i];
|
||||
}
|
||||
free (table);
|
||||
}
|
||||
|
||||
unsigned int HashTable::HashString (const char * str)
|
||||
{
|
||||
unsigned int retval = 0;
|
||||
int length = strlen(str);
|
||||
|
||||
unsigned int top5bits = 0xf8000000;
|
||||
unsigned int carry = 0;
|
||||
|
||||
const int kleftmove=5;
|
||||
const int krightmove=27;
|
||||
|
||||
for (int i=0; i<length; i++)
|
||||
{
|
||||
carry = retval & top5bits;
|
||||
carry = carry >> 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;
|
||||
}
|
||||
30
hashtbl.h
Normal file
30
hashtbl.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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 ;)
|
||||
};
|
||||
11
hashtest.cpp
Normal file
11
hashtest.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include <iostream>
|
||||
#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;
|
||||
}
|
||||
|
||||
169
inet6.c
Normal file
169
inet6.c
Normal file
@@ -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, <waltje@uwalt.nl.mugnet.org>
|
||||
* 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 <asm/types.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <arpa/nameser.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <resolv.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
/*
|
||||
#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"
|
||||
};
|
||||
226
inodeproc.cpp
Normal file
226
inodeproc.cpp
Normal file
@@ -0,0 +1,226 @@
|
||||
/* this comes from netstat.c, but is very useful :)) */
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
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;pnp<prg_hash+PRG_HASH_SIZE;pnp++)
|
||||
while ((pn=*pnp)) {
|
||||
*pnp=pn->next;
|
||||
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");
|
||||
}
|
||||
|
||||
27
nethogs.8
Normal file
27
nethogs.8
Normal file
@@ -0,0 +1,27 @@
|
||||
.\" This page Copyright (C) 2004 Fabian Frederick <fabian.frederick@gmx.fr>
|
||||
.\" 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 <arnouten@bzzt.net>.
|
||||
175
nethogs.cpp
Normal file
175
nethogs.cpp
Normal file
@@ -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 <iostream>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
|
||||
#include <ncurses.h>
|
||||
|
||||
extern "C" {
|
||||
#include <pcap.h>
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
28
nethogs.h
Normal file
28
nethogs.h
Normal file
@@ -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
|
||||
178
packet.cpp
Normal file
178
packet.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
#include <iostream>
|
||||
#include "packet.h"
|
||||
#include <netinet/tcp.h>
|
||||
#include <netinet/in.h>
|
||||
#include <malloc.h>
|
||||
#include <assert.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
#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)));
|
||||
}
|
||||
45
packet.h
Normal file
45
packet.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef __PACKET_H
|
||||
#define __PACKET_H
|
||||
|
||||
#define _BSD_SOURCE 1
|
||||
#include <net/ethernet.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <pcap.h>
|
||||
}
|
||||
|
||||
/* 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
|
||||
463
process.cpp
Normal file
463
process.cpp
Normal file
@@ -0,0 +1,463 @@
|
||||
#include <iostream>
|
||||
#include <strings.h>
|
||||
#include <string>
|
||||
#include <ncurses.h>
|
||||
#include <asm/types.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdlib.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#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; i<nproc; i++)
|
||||
{
|
||||
lines[i]->show(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();
|
||||
}
|
||||
72
process.h
Normal file
72
process.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef __PROCESS_H
|
||||
#define __PROCESS_H
|
||||
|
||||
#include <assert.h>
|
||||
#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
|
||||
17
refresh.cpp
Normal file
17
refresh.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include <iostream>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#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);
|
||||
}
|
||||
|
||||
0
structs.cpp
Normal file
0
structs.cpp
Normal file
Reference in New Issue
Block a user