/*
 * tcptop.c
 * (c) 2004 adam wysocki
 * <gophi@kernel.org.pl>
 *
 * proste, fajne narzędzie a'la top, pozwalające określić, kto z lokalnych 
 * luserów ile zużywa pasma na danym interfejsie ethernetowym (inne nie są 
 * obsługiwane, bo zwyczajnie tego nie potrzebowałem, jeśli komuś bardzo 
 * zależy na innym arptypie, to niech da znać). niestety przechwytuje ramki 
 * ethernetowe, więc wymaga róta albo przynajmniej CAP_NET_RAW (jeśli ktoś 
 * używa mojej starej, zapomnianej łatki na jądro, wyłączającej luserom 
 * netstatowanie, to będzie potrzebował roota ze względu na dostęp do 
 * odpowiedniego wpisu w /proc). nie wymaga libpcapa, bo korzysta 
 * bezpośrednio z packet socketów, wymaga natomiast przynajmniej jądra 
 * 2.2.x (jeśli będzie potrzeba, to dodam obsługę starszych kerneli, 
 * ale get real, kto tego używa?).
 *
 * jak działa? względnie prosto. najpierw na podstawie tablicy połączeń 
 * ipv4, którą kernel udostępnia w /proc/net/tcp, budowana jest lista 
 * opisująca kolejnych użytkowników, mających przynajmniej jedno aktywne 
 * połączenie, są im przypisywane liczniki oraz listy parametrów wszystkich 
 * aktywnych połączeń. potem otwierany jest packet socket, sprawdzany jest 
 * typ interfejsu, packet socket jest do niego bindowany i rozpoczyna się 
 * pobieranie z niego ramek ethernetowych. każda prawidłowa (tzn. będąca 
 * częścią protokołu tcp, co jest weryfikowane przez sprawdzanie najpierw 
 * rozmiaru ramki, a potem pól określających następne protokoły na kolejnych 
 * warstwach enkapsulacji) ramka jest obierana z enkapsulacji warstwy 
 * datalinku, a nagłówki enkapsulacji ip i tcp służą do porównania 
 * paramerów ze wszystkimi elementami list połączeń przypisanych do 
 * każdego uida w procesie budowania listy połączeń (wyżej). jeśli ramka 
 * pasuje do któregoś elementu którejś listy połączeń, to zwiększane są 
 * liczniki danego uida (każdy uid ma cztery liczniki, bajty wchodzące, 
 * wychodzące oraz analogicznie dla pakietów). licznik wybierany jest na 
 * podstawie porównania adresu (i portu) źródłowego pakietu z adresem 
 * i portem lokalnym listy połączeń kernela. po pewnym (określonym) czasie 
 * packet socket jest zamykany, a lista użytkowników jest sortowana przez 
 * zastosowanie prostego algorytmu, który w pętli ustawia wskaźnik do 
 * elementu listy użytkowników na pierwszy element bez przyznanego indeksu, 
 * potem w zagnieżdżonej pętli wybiera spośród elementów bez przyznanego 
 * indeksu "najwyższy" element i przyporządkuje do niego wskaźnik, a po 
 * przejściu tej zagnieżdżonej pętli przyznaje elementowi listy, wskazywanemu 
 * przez ten wskaźnik, kolejny indeks. pętla zewnętrzna jest wykonywana do 
 * czasu, aż wszystkie elementy listy będą miały przyznane indeksy. pewnie 
 * ktoś już kiedyś wymyślił ten algorytm i nosi jakąś nazwę, ja rozpisałem 
 * go na poczekaniu i wygląda na to że działa :) potem lista jest tworzona 
 * w tymczasowym buforze w kolejności zależnej od wybranego kierunku oraz 
 * klucza sortowania przez sprawdzenie przy każdym elemencie, czy powinien 
 * zostać wypisany w danej iteracji pętli wypisującej, zgodnie z warunkiem 
 * logicznym (zakładając, że sort_key to wybrany klucz sortowania, i to numer 
 * iteracji, uptr->sort_index to nadany elementowi indeks, max_index to 
 * maksymalny nadany indeks, & to iloczyn bitowy, && iloczyn logiczny, 
 * == równoważność, a || suma logiczna): ((!(sort_key & 8) && uptr->sort_index 
 * == (i + 1)) || ((sort_key & 8) && ((max_index - uptr->sort_index) == i))). 
 * po utworzeniu listy w tym buforze jego zawartość jest wypisywana na 
 * terminalu.
 *
 * changelog:
 *
 *  * 29.05.2004 v1.4 dopisanie wyświetlania podsumowania i "poczekaj na 
 *    blabla" przy starcie.
 *
 *  * 14.05.2004 v1.3 naprawienie segfaultu w przypadku, kiedy użytkownik 
 *    zostanie usunięty podczas działania programu.
 *
 *  * 14.05.2004 v1.2 poprawiona obsługa pamięci, dodane buforowanie 
 *    podczas wypisywania posortowanych danych, tak żeby zredukować 
 *    migotanie na słabszych maszynach.
 *
 *  * 13.05.2004 v1.1 poprawienie paru błędów w obsłudze packet socket.
 *
 *  * 07.05.2004 v1.0 wersja pierwsza, dziewicza.
 *
 * bugs:
 *
 *  * tak, wiem, program kiepsko radzi sobie z krótkimi połączeniami, 
 *    bo może ich po prostu nie zauważyć, ale ogólnie chyba może być.
 *
 *  * z przyczyn mi nieznanych jeśli packet socket zostanie otwarty do 
 *    przechwytywania jedynie ramek protokołu IP, to nie przechwytuje 
 *    ramek wychodzących. poprawiłem na przechwytywanie ramek wszystkich 
 *    protokołów i ręczne odrzucanie, ale to chyba nie jest rozwiązanie...
 *
 * todo:
 *
 *  * support dla innych interfejsów, niż ethernetowe (jeśli ktoś bardzo 
 *    będzie tego potrzebował).
 *
 *  * dodanie obsługi ipv6 (również jeśli ktoś będzie potrzebował).
 *
 *  * dodanie obsługi fragmentacji ip (!!!).
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <pwd.h>
#include <asm/types.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/if.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/sockios.h>
#include <features.h>
#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1
#	include <netpacket/packet.h>
#	include <net/ethernet.h>
#endif

#define panic(...) _panic (__LINE__, __VA_ARGS__)

enum {
	SORT_BT = 0,	/* bytes total */
	SORT_BI,	/* bytes in */
	SORT_BO,	/* bytes out */
	SORT_PT,	/* packets total */
	SORT_PI,	/* packets in */
	SORT_PO,	/* packets out */
	SORT_UID,	/* uid */
	SORT_UNAME,	/* username */
	SORT_MAX	/* musi być ostatnie */
};

extern int errno;
extern char *optarg;
extern int optind, opterr, optopt;

char *progname = "tcptop";

char *netdev = "eth0";
char onepass = 0;	/* czy jeden pomiar czy ciągle */
char count_dlink = 0;	/* czy wliczać enkapsulację datalinku */
char sort_key = SORT_BT;
int meas_time = 5;	/* czas trwania jednego pomiaru */
int num_to_show = 20;	/* liczba userów do wypisania */

int signo = 0;

struct conn_type {
	__u32 laddr, faddr;
	__u16 lport, fport;
	struct conn_type *next;
};

struct user_type {
	unsigned short uid;
	char *username;
	struct conn_type *conns;
	unsigned long bytes_in, bytes_out;
	unsigned long packets_in, packets_out;
	int sort_index;
	struct user_type *next;
} *users;

void sighnd (int _signo)
{
	signo = _signo;
	signal (_signo, sighnd);
}

void _panic (int line, char *fmt, ...)
{
	va_list args;
	char buf[1024];

	va_start (args, fmt);
	vsnprintf (buf, sizeof(buf), fmt, args);
	va_end (args);

	fprintf (stderr, "tcptop[%u]:%d: %s\n", getpid(), line, buf);
	_exit (-1);
}

void *xmalloc (size_t size)
{
	void *ptr;

	ptr = malloc(size);

	if (!ptr)
		panic ("xmalloc: size=%lu", size);

	return ptr;
}

void *xrealloc (void *ptr, size_t size)
{
	ptr = realloc(ptr, size);

	if (!ptr)
		panic ("xrealloc: ptr=%X, size=%lu", ptr, size);

	return ptr;
}

void set_progname (char **argv)
{
	if (!argv || !argv[0])
		return;

	progname = strrchr(argv[0], '/');
	if (progname)
		progname++;
	else
		progname = argv[0];
}

/*
 * otwiera socketa, binduje go do interfejsu, zwraca fd.
 */

int init_socket (void)
{
	struct ifreq req;
	int sockfd;
	struct sockaddr_ll sll;

/*	sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));	xxx */
	sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
	if (sockfd == -1)
		panic ("socket: %m");

	/* cześć interfejsie, czym jesteś? */

	bzero (&req, sizeof(req));
	strncpy (req.ifr_name, netdev, sizeof(req.ifr_name));
	if (ioctl(sockfd, SIOCGIFHWADDR, &req) == -1)
		panic ("ioctl SIOCGIFHWADDR: %m");

	switch (req.ifr_ifru.ifru_hwaddr.sa_family) {
		case ARPHRD_ETHER:
		case ARPHRD_METRICOM:
		/* case ARPHRD_LOOPBACK: */
			break;
		default:
			panic ("Niestety tylko ethernet jest obsługiwany.");
	}

	/* a jaki jest twój numerek? */

	bzero (&req, sizeof(req));
	strncpy (req.ifr_name, netdev, sizeof(req.ifr_name));
	if (ioctl(sockfd, SIOCGIFINDEX, &req) == -1)
		panic ("ioctl SIOCGIFINDEX: %m");

	/* okej, pogadaj z socketem... */

	bzero (&sll, sizeof(sll));
	sll.sll_family = AF_PACKET;
	sll.sll_ifindex = req.ifr_ifru.ifru_ivalue;
/*	sll.sll_protocol = htons(ETH_P_IP);	xxx */
	sll.sll_protocol = htons(ETH_P_ALL);
	if (bind(sockfd, (struct sockaddr *) &sll, sizeof(sll)) == -1)
		panic ("bind: %m");

	/* no i... tyle :) */

	return sockfd;
}

/*
 * czyta ramkę do buf (maksimum buf_len bajtów), zwraca (uwaga!) 
 * rzeczywisty rozmiar ramki (albo ilość zapisanych bajtów, jeśli 
 * mniejsza od buf_len).
 */

int read_frame (int sockfd, char *buf, int buf_len)
{
	struct sockaddr_ll from;
	struct sll_header *hdr;
	socklen_t from_len;
	int packet_len;
	struct timeval tv;
	fd_set rdfds;
	int rs;

	do {	/* bo może przyjść podczas syscalla sygnał */
		FD_ZERO (&rdfds);
		FD_SET (sockfd, &rdfds);
		/* tutaj a nie w pętli niżej, bo to linux, 
		 * więc select przy przerwaniu aktualizuje
		 * liczbę w tv. */
		tv.tv_sec = 0;
		tv.tv_usec = 50000;	/* 50 ms */
		while (1) {
			rs = select(sockfd + 1, &rdfds, NULL, NULL, &tv);
			if (!rs)	/* timeout */
				return 0;
			else if (rs != -1)	/* jest okej */
				break;
			/* nie jest okej. sygnał? */
			if (errno == EINTR)
				continue;
			panic ("select: %m");
		}
		from_len = sizeof(from);
		packet_len = recvfrom(sockfd, buf, buf_len, MSG_TRUNC, (struct sockaddr *) &from, &from_len);
	} while (packet_len == -1 && errno == EINTR);

	if (packet_len == -1)
		if (errno == EAGAIN)	/* a jednak nie ma pakietu? */
			return 0;
		else
			panic ("recvfrom: %m");

	/* oKeŚ */

	return packet_len;
}

void fasthelp (void)
{
	printf ("-h - ten helpskrin\n");
	printf ("-i interfejs (domyślnie eth0)\n");
	printf ("-b - wykonanie jednego przebiegu i wyjście\n");
	printf ("-c - wliczanie enkapsulacji datalinku do statystyk\n");
	printf ("-t czas - czas jednego pomiaru (domyślnie 5 sekund)\n");
	printf ("-n ile - liczba userów do wypisania, 0 - no limit (domyślnie 20)\n");
	printf ("-s numb - klucz sortowania (domyślnie 0):\n");
	printf ("   0 - bytes total\n");
	printf ("   1 - bytes in\n");
	printf ("   2 - bytes out\n");
	printf ("   3 - packets total\n");
	printf ("   4 - packets in\n");
	printf ("   5 - packets out\n");
	printf ("   6 - uid\n");
	printf ("   7 - username\n");
	printf ("   (dodanie 8 powoduje odwrócenie kolejności sortowania)\n");
}

void parse_options (int argc, char **argv)
{
	char done = 0;

	while (!done) switch(getopt(argc, argv, "hi:bcs:t:n:")) {
		case ':':
		case '?':
			panic ("parse_options: Błąd w linii komend");
		case -1:
			done = 1;
			break;
		case 'h':
			fasthelp();
			_exit (0);
		case 'i':
			netdev = optarg;
			break;
		case 'b':
			onepass = 1;
			break;
		case 'c':
			count_dlink = 1;
			break;
		case 's':
			sort_key = atoi(optarg);
			if (sort_key >= SORT_MAX * 2)
				panic ("parse_options: sort_key >= SORT_MAX * 2");
			break;
		case 'n':
			num_to_show = atoi(optarg);
			break;
		case 't':
			meas_time = atoi(optarg);
			if (!meas_time)
				panic ("parse_options: !meas_time");
			break;
		default:
			panic ("parse_options: nieoczekiwane zachowanie getopt...?");
	}
}

long uid_index (int uid)
{
	struct user_type *uptr = users;
	int i = 0;

	while (uptr)
		if (uptr->uid == uid)
			return i;
		else {
			i++;
			uptr = uptr->next;
		}

	return -1;
}

long add_uid (int uid)
{
	struct user_type *uptr = users, *prev = (struct user_type *) NULL;
	struct passwd *pw;
	long i = 0;

	while (uptr) {
		prev = uptr;
		uptr = uptr->next;
		i++;
	}

	if (prev)
		uptr = prev->next = (struct user_type *) xmalloc(sizeof(struct user_type));
	else
		uptr = users = (struct user_type *) xmalloc(sizeof(struct user_type));

	pw = getpwuid(uid);
	uptr->uid = uid;
	if (pw)
		uptr->username = strdup(pw->pw_name);
	else
		uptr->username = strdup("USUNIETY");
	uptr->conns = (struct conn_type *) NULL;
	uptr->bytes_in = uptr->bytes_out = 0;
	uptr->packets_in = uptr->packets_out = 0;
	uptr->sort_index = 0;
	uptr->next = (struct user_type *) NULL;

	if (!uptr->username)
		panic ("strdup: %m");

	return i;
}

void add_to_list (long index, struct conn_type *conn)
{
	struct user_type *uptr = users;
	struct conn_type *cptr, *prev = (struct conn_type *) NULL;

	while (index--)
		uptr = uptr->next;

	if (!uptr)
		panic ("add_to_list: !uptr");	/* o_0 */

	cptr = uptr->conns;
	while (cptr) {
		prev = cptr;
		cptr = cptr->next;
	}

	if (prev)
		cptr = prev->next = (struct conn_type *) xmalloc(sizeof(struct conn_type));
	else
		cptr = uptr->conns = (struct conn_type *) xmalloc(sizeof(struct conn_type));

	memcpy (cptr, conn, sizeof(struct conn_type));
}

void add_entry (char *line)
{
	long index;
	__u32 loc_ip, rem_ip;
	__u16 loc_p, rem_p;
	unsigned char state;
	unsigned int uid;
	struct conn_type conn;

	sscanf (line, "%*u: %X:%X %X:%X %X %*X:%*X %*X:%*X %*X %u", 
		&conn.laddr, &conn.lport, &conn.faddr, &conn.fport, &state, &uid);
	if (state != TCP_ESTABLISHED)
		return;

	conn.lport = ntohs(conn.lport);
	conn.fport = ntohs(conn.fport);
	index = uid_index(uid);
	if (index == -1)
		index = add_uid(uid);
	conn.next = (struct conn_type *) NULL;
	add_to_list (index, &conn);
}

void init_users (void)
{
	FILE *fp;
	char linebuf[1024];
	char done = 0;

	users = (struct user_type *) NULL;

	fp = fopen("/proc/net/tcp", "r");
	if (!fp)
		panic ("fopen /proc/net/tcp: %m");

	fgets (linebuf, 1024, fp);	/* zjedzenie nagłówka */

	while (!done) {
		fgets (linebuf, 1024, fp);
		if (feof(fp))
			done = 1;
		else
			add_entry (linebuf);
	}

	fclose (fp);
}

void free_conns (struct conn_type *conns)
{
	struct conn_type *tmp;

	while (conns) {
		tmp = conns;
		conns = conns->next;
		free (tmp);
	}	
}

void free_users (void)
{
	struct user_type *uptr = users, *tmp;

	while (uptr) {
		free (uptr->username);
		free_conns (uptr->conns);
		tmp = uptr;
		uptr = uptr->next;
		free (tmp);
	}
}

char encap_matches (struct conn_type *key, struct conn_type *encap)
{
	struct in_addr dupa;

	if (key->laddr == encap->laddr && key->faddr == encap->faddr 
		&& key->lport == encap->lport && key->fport == encap->fport)
		return 1;	/* local->remote */
	else if (key->laddr == encap->faddr && key->faddr == encap->laddr 
		&& key->lport == encap->fport && key->fport == encap->lport)
		return 2;	/* remote->local */

	return 0;	/* nagłówki nie pasują */
}

char count_encap (struct user_type *uptr, struct conn_type *encap, int len)
{
	struct conn_type *cptr = uptr->conns;
	char in_out;

	while (cptr) switch (encap_matches(cptr, encap)) {
		case 0:	/* nagłówki nie pasują */
			cptr = cptr->next;
			break;
		case 1:	/* local->remote */
			uptr->bytes_out += len;
			uptr->packets_out++;
			return 1;
		case 2:	/* remote->local */
			uptr->bytes_in += len;
			uptr->packets_in++;
			return 1;
	}

	return 0;
}

char is_more_first (struct user_type *u1, struct user_type *u2)
{
	unsigned long val[2];

	if (!u2)
		return 1;

	switch (sort_key & 7) {
		case SORT_BT:
			val[0] = u1->bytes_in + u1->bytes_out;
			val[1] = u2->bytes_in + u2->bytes_out;
			break;
		case SORT_BI:
			val[0] = u1->bytes_in;
			val[1] = u2->bytes_in;
			break;
		case SORT_BO:
			val[0] = u1->bytes_out;
			val[1] = u2->bytes_out;
			break;
		case SORT_PT:
			val[0] = u1->packets_in + u1->packets_out;
			val[1] = u2->packets_in + u2->packets_out;
			break;
		case SORT_PI:
			val[0] = u1->packets_in;
			val[1] = u2->packets_in;
			break;
		case SORT_PO:
			val[0] = u1->packets_out;
			val[1] = u2->packets_out;
			break;
		case SORT_UID:
			val[1] = u1->uid;
			val[0] = u2->uid;
			break;
		case SORT_UNAME:
			val[1] = 1;
			if (strcasecmp(u1->username, u2->username) > 0)
				val[0] = 0;
			else
				val[0] = 2;
			break;
	}

	return val[0] > val[1] ? 1 : 0;
}

/*
 * algorytm sortowania jest może trochę dziwny... nie wiem czy nosi 
 * jakąś nazwę, pewnie już ktoś go kiedyś wymyślił. robimy w pętli, 
 * aż każdy element tablicy będzie miał przyporządkowany indeks:
 *
 * 1. ustawienie wskaźnika na aktualny "najpierwszy" element na pierwszy 
 *    element bez przyznanego indeksu.
 * 2. w pętli sprawdzenie wszystkich elementów bez przyznanego indeksu 
 *    i wybranie z nich "najbardziej pierwszego" elementu.
 * 3. przyznanie mu kolejnego indeksu.
 *
 */

void sort_users (void)
{
	struct user_type *uptr, *first_ptr;
	int index = 0;
	char done = 0;

	while (!done) {
		done = 1;
		uptr = users;
		first_ptr = (struct user_type *) NULL;
		while (uptr) {
			if (!uptr->sort_index) {
				done = 0;
				if (is_more_first(uptr, first_ptr))
					first_ptr = uptr;
			}
			uptr = uptr->next;
		}
		if (!done)
			first_ptr->sort_index = ++index;
	}
}

char *get_count (unsigned long bigval)
{
	static char buf[8];

	if (bigval < (1 << 10))
		sprintf (buf, "%lu", bigval);
	else if (bigval >= (1 << 10) && bigval < (1 << 20))
		sprintf (buf, "%luk", bigval >> 10);
	else if (bigval >= (1 << 30) && bigval < (1 << 30))
		sprintf (buf, "%luM", bigval >> 20);
	else
		sprintf (buf, "%luG", bigval >> 30);

	return buf;
}

void show_users (void)
{
	struct user_type total;
	int i, max_index = 0, to_show;
	struct user_type *uptr;
	char printbuf[40];
	char *buf = (char *) NULL;
	int buflen = 0, written, ommited;

	bzero (&total, sizeof(struct user_type));

	uptr = users;
	while (uptr) {
		total.bytes_in += uptr->bytes_in;
		total.bytes_out += uptr->bytes_out;
		total.packets_in += uptr->packets_in;
		total.packets_out += uptr->packets_out;
		max_index++;
		uptr = uptr->next;
	}

	if (max_index < num_to_show)
		to_show = max_index;
	else
		to_show = num_to_show;

	ommited = num_to_show - to_show;

	for (i = 0; i < to_show; i++) {
		uptr = users;
		while (uptr) {
			if ((!(sort_key & 8) && uptr->sort_index == (i + 1)) || ((sort_key & 8) && ((max_index - uptr->sort_index) == i)))
				break;
			else
				uptr = uptr->next;
		}
		if (!uptr)
			panic ("bug w funkcji sortującej...?");
		if (!(uptr->packets_in + uptr->packets_out)) {
			ommited++;
			continue;
		}

		written = snprintf(printbuf, sizeof(printbuf), "%5u %-8s ", uptr->uid, uptr->username);
		buf = (char *) xrealloc(buf, buflen + written);
		memcpy (buf + buflen, printbuf, written);
		buflen += written;
		written = snprintf(printbuf, sizeof(printbuf), "%8s ", get_count(uptr->bytes_in / meas_time));
		buf = (char *) xrealloc(buf, buflen + written);
		memcpy (buf + buflen, printbuf, written);
		buflen += written;
		written = snprintf(printbuf, sizeof(printbuf), "%8s ", get_count(uptr->bytes_out / meas_time));
		buf = (char *) xrealloc(buf, buflen + written);
		memcpy (buf + buflen, printbuf, written);
		buflen += written;
		written = snprintf(printbuf, sizeof(printbuf), "%8s ", get_count((uptr->bytes_in + uptr->bytes_out) / meas_time));
		buf = (char *) xrealloc(buf, buflen + written);
		memcpy (buf + buflen, printbuf, written);
		buflen += written;
		written = snprintf(printbuf, sizeof(printbuf), "%8s ", get_count(uptr->packets_in / meas_time));
		buf = (char *) xrealloc(buf, buflen + written);
		memcpy (buf + buflen, printbuf, written);
		buflen += written;
		written = snprintf(printbuf, sizeof(printbuf), "%8s ", get_count(uptr->packets_out / meas_time));
		buf = (char *) xrealloc(buf, buflen + written);
		memcpy (buf + buflen, printbuf, written);
		buflen += written;
		written = snprintf(printbuf, sizeof(printbuf), "%8s\n", get_count((uptr->packets_in + uptr->packets_out) / meas_time));
		buf = (char *) xrealloc(buf, buflen + written);
		memcpy (buf + buflen, printbuf, written);
		buflen += written;
	}

	if (!onepass)
		printf ("\e[H");

	printf ("  UID USER      BAND_IN BAND_OUT BAND_TOT  PACK_IN PACK_OUT PACK_TOT\n");
	printf ("      TOTAL    ");
	printf ("%8s ", get_count(total.bytes_in / meas_time));
	printf ("%8s ", get_count(total.bytes_out / meas_time));
	printf ("%8s ", get_count((total.bytes_in + total.bytes_out) / meas_time));
	printf ("%8s ", get_count(total.packets_in / meas_time));
	printf ("%8s ", get_count(total.packets_out / meas_time));
	printf ("%8s\n", get_count((total.packets_in + total.packets_out) / meas_time));

	if (buf) {
		buf = (char *) xrealloc(buf, buflen + 1);
		buf[buflen] = (char) NULL;
		printf ("%s", buf);
		free (buf);
		for (i = 0; i < ommited; i++)
			printf ("                                                                    \n");
	}
}

void make_meas (void)
{
#define ENCAP_SIZE (sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct tcphdr))
	int sockfd;
	int len;
	unsigned char frame[ENCAP_SIZE];
	struct ethhdr *eth;
	struct iphdr *ip;
	struct tcphdr *tcp;
	struct conn_type encap;
	struct user_type *uptr;

	init_users();
	sockfd = init_socket();
	alarm (meas_time);
	signal (SIGALRM, sighnd);

	while (!signo) {
		len = read_frame(sockfd, frame, sizeof(frame));
		if (len < ENCAP_SIZE)
			continue;
		eth = (struct ethhdr *) frame;
		if (eth->h_proto != htons(ETH_P_IP))
			continue;
		ip = (struct iphdr *) (frame + sizeof(struct ethhdr));
		if (ip->protocol != IPPROTO_TCP)
			continue;
		tcp = (struct tcphdr *) (frame + sizeof(struct ethhdr) + (ip->ihl << 2));
		encap.laddr = ip->saddr;
		encap.faddr = ip->daddr;
		encap.lport = tcp->source;
		encap.fport = tcp->dest;
		uptr = users;
		while (uptr)
			if (count_encap(uptr, &encap, len))
				break;
			else
				uptr = uptr->next;
	}

	close (sockfd);
	if (!count_dlink) {
		uptr = users;
		while (uptr) {
			if (uptr->bytes_in >= (uptr->packets_in * sizeof(struct ethhdr)))
				uptr->bytes_in -= (uptr->packets_in * sizeof(struct ethhdr));
			if (uptr->bytes_out >= (uptr->packets_out * sizeof(struct ethhdr)))
				uptr->bytes_out -= (uptr->packets_out * sizeof(struct ethhdr));
			uptr = uptr->next;
		}
	}
	if (!signo || signo == SIGALRM) {
		sort_users();
		show_users();
		signo = 0;
	}
	free_users();
}

int main (int argc, char **argv)
{
	set_progname (argv);
	parse_options (argc, argv);

	signal (SIGTERM, sighnd);
	signal (SIGINT, sighnd);

	printf ("Poczekaj na wykonanie pierwszego pomiaru.\n");

	if (onepass) {
		make_meas();
		goto term;
	} else
		printf ("\e[H\e[J");

	while (!signo)
		make_meas();

term:
	signal (SIGINT, SIG_DFL);
	signal (SIGTERM, SIG_DFL);
	return 0;
}
