/*
* Copyright (c) 2004 Rinet Corp., Novosibirsk, Russia
*
* Redistribution and use in source forms, with and without modification,
* are permitted provided that this entire comment appears intact.
*
* THIS SOURCE CODE IS PROVIDED ``AS IS'' WITHOUT ANY WARRANTIES OF ANY KIND.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include "netstat.h"
#include "hashtab.h"
#include "trafshow.h"
#include "events.h" /* just for tv_diff() */
#include "colormask.h"
#include "addrtoname.h"
int
netstat_count(ph)
const PCAP_HANDLER *ph;
{
/* sanity check */
if (!ph || !ph->ns_hash)
return 0;
return hcount(ph->ns_hash);
}
static void
maskit(bp, len, bits)
u_int8_t *bp;
int len, bits;
{
register u_int8_t mask;
register int i, j;
for (i = 0; i < len; i++) {
mask = 0;
for (j = 0; j < 8 && bits > 0; j++, bits--) {
mask >>= 1;
mask |= 0x80;
}
bp[i] &= mask;
}
}
void
netstat_aggregate(nh, bits)
struct netstat_header *nh;
int bits;
{
/* sanity check */
if (!nh || bits < 0)
return;
memset(&nh->en_hdr.src, 0, sizeof(nh->en_hdr.src));
memset(&nh->en_hdr.dst, 0, sizeof(nh->en_hdr.dst));
if (nh->in_hdr.ver) {
struct ip_address *src = &nh->in_hdr.src;
struct ip_address *dst = &nh->in_hdr.dst;
maskit((u_int8_t *)&src->ipaddr, sizeof(src->ipaddr), bits);
maskit((u_int8_t *)&dst->ipaddr, sizeof(dst->ipaddr), bits);
/* guess server port */
if (src->ip_port && dst->ip_port) {
u_int16_t sport = ntohs(src->ip_port);
u_int16_t dport = ntohs(dst->ip_port);
if (isservport(sport))
dst->ip_port = 0;
else if (isservport(dport))
src->ip_port = 0;
else if (sport < IPPORT_RESERVED)
dst->ip_port = 0;
else if (dport < IPPORT_RESERVED)
src->ip_port = 0;
else if (sport >= IPPORT_DYNAMIC)
src->ip_port = 0;
else if (dport >= IPPORT_DYNAMIC)
dst->ip_port = 0;
else if (sport > dport)
src->ip_port = 0;
else dst->ip_port = 0;
}
}
}
static int
htab_insert(ht, ns)
struct htab *ht;
const NETSTAT *ns;
{
ub1 *key;
ub4 keyl;
int op;
NETSTAT *dp;
key = (ub1 *)&ns->ns_hdr;
keyl = sizeof(ns->ns_hdr);
if ((op = hadd(ht, key, keyl, 0)) < 0)
return -1;
if (op) { /* OK, new item inserted */
if ((dp = (NETSTAT *)malloc(sizeof(NETSTAT))) == 0) {
hdel(ht);
return -1;
}
memcpy(dp, ns, sizeof(NETSTAT));
dp->gain_pkt_cnt = ns->pkt_cnt;
dp->gain_pkt_len = ns->pkt_len;
dp->gain_data_len = ns->data_len;
dp->attr = colormask(&dp->ns_hdr);
hkey(ht) = (ub1 *)&dp->ns_hdr;
hstuff(ht) = dp;
return 1;
}
/* Failed because already in cache -- update it */
if ((dp = (NETSTAT *)hstuff(ht)) == 0)
return 0; /* should not happen */
dp->pkt_cnt += ns->pkt_cnt;
dp->pkt_len += ns->pkt_len;
dp->data_len += ns->data_len;
dp->gain_pkt_cnt += ns->pkt_cnt;
dp->gain_pkt_len += ns->pkt_len;
dp->gain_data_len += ns->data_len;
if (ns->pkt_cnt_rate || ns->pkt_len_rate || ns->data_len_rate) {
dp->mtime = ns->mtime;
dp->pkt_cnt_rate = ns->pkt_cnt_rate;
dp->pkt_len_rate = ns->pkt_len_rate;
dp->data_len_rate = ns->data_len_rate;
} else if ((op = tv_diff(&dp->mtime, &ns->mtime)) >= 1000) {
dp->mtime = ns->mtime;
dp->gain_pkt_cnt = dp->gain_pkt_cnt * 1000 / op;
if (dp->gain_pkt_cnt) {
dp->pkt_cnt_rate = dp->gain_pkt_cnt;
dp->gain_pkt_cnt = 0;
}
dp->gain_pkt_len = dp->gain_pkt_len * 1000 / op;
if (dp->gain_pkt_len) {
dp->pkt_len_rate = dp->gain_pkt_len;
dp->gain_pkt_len = 0;
}
dp->gain_data_len = dp->gain_data_len * 1000 / op;
if (dp->gain_data_len) {
dp->data_len_rate = dp->gain_data_len;
dp->gain_data_len = 0;
}
}
return 0;
}
int
netstat_insert(ph, ns)
PCAP_HANDLER *ph;
const NETSTAT *ns;
{
int op;
NETSTAT ns_buf;
/* sanity check */
if (!ph || !ns) {
errno = EINVAL;
return -1;
}
if (!ph->ns_hash && (ph->ns_hash = hcreate(65536)) == 0)
return -1;
if (ph->masklen >= 0) {
memcpy(&ns_buf, ns, sizeof(NETSTAT));
netstat_aggregate(&ns_buf.ns_hdr, ph->masklen);
ns = &ns_buf;
}
if (ph->ns_mutex) pthread_mutex_lock(ph->ns_mutex);
op = htab_insert(ph->ns_hash, ns);
if (ph->ns_mutex) pthread_mutex_unlock(ph->ns_mutex);
return op;
}
int
netstat_find(ph, ns)
PCAP_HANDLER *ph;
NETSTAT *ns; /* IN/OUT */
{
struct htab *ht;
ub1 *key;
ub4 keyl;
NETSTAT *found;
int ok = 0;
/* sanity check */
if (!ph || !ns || netstat_count(ph) < 1)
return 0;
if (ph->ns_mutex) pthread_mutex_lock(ph->ns_mutex);
ht = ph->ns_hash;
key = (ub1 *)&ns->ns_hdr;
keyl = sizeof(ns->ns_hdr);
if (hfind(ht, key, keyl) && (found = hstuff(ht)) != 0) {
ok = 1;
*ns = *found;
}
if (ph->ns_mutex) pthread_mutex_unlock(ph->ns_mutex);
return ok;
}
int
netstat_purge(ph, at)
PCAP_HANDLER *ph;
const struct timeval *at;
{
struct htab *ht;
int op, cnt = 0;
NETSTAT *ns;
/* sanity check */
if (!ph) {
errno = EINVAL;
return -1;
}
if (netstat_count(ph) < 1)
return 0;
if (ph->ns_mutex) pthread_mutex_lock(ph->ns_mutex);
ht = ph->ns_hash;
op = hfirst(ht);
while (op && hcount(ht) > 0) {
ns = hstuff(ht);
if (!ns) { /* should not happen */
op = hdel(ht);
} else if (!at || timercmp(&ns->mtime, at, <)) {
free(ns);
op = hdel(ht);
cnt++;
} else {
op = hnext(ht);
}
}
if (ph->ns_mutex) pthread_mutex_unlock(ph->ns_mutex);
return cnt;
}
void
netstat_free(ph)
PCAP_HANDLER *ph;
{
struct htab *ht;
/* sanity check */
if (!ph) return;
netstat_purge(ph, 0);
if (ph->ns_mutex) pthread_mutex_lock(ph->ns_mutex);
ht = ph->ns_hash;
ph->ns_hash = 0;
if (ht) hdestroy(ht);
if (ph->ns_mutex) pthread_mutex_unlock(ph->ns_mutex);
}
int
netstat_match(p1, p2)
register const NETSTAT *p1, *p2;
{
/* sanity check */
if (!p1 || !p2) return 0;
return !memcmp(&p1->ns_hdr, &p2->ns_hdr, sizeof(struct netstat_header));
}
int
netstat_bidir(p1, p2)
register const NETSTAT *p1, *p2;
{
/* sanity check */
if (!p1 || !p2) return 0;
if (p1->ip_ver) {
if (p1->ip_ver == p2->ip_ver &&
p1->ip_proto == p2->ip_proto &&
!memcmp(&p1->ip_src_addr, &p2->ip_dst_addr,
sizeof(struct ip_address)) &&
!memcmp(&p2->ip_src_addr, &p1->ip_dst_addr,
sizeof(struct ip_address)))
return 1;
} else if (!p2->ip_ver) {
if (p1->eth_type == p2->eth_type &&
!memcmp(p1->eth_src_addr, p2->eth_dst_addr,
ETHER_ADDR_LEN) &&
!memcmp(p2->eth_src_addr, p1->eth_dst_addr,
ETHER_ADDR_LEN))
return 1;
}
return 0;
}
int
netstat_fetch(list, ph)
NETSTAT **list[];
PCAP_HANDLER *ph;
{
struct htab *ht;
int op, cnt, i;
NETSTAT *ns, **array;
/* sanity check */
if (!list || !ph) {
errno = EINVAL;
return -1;
}
if ((cnt = netstat_count(ph)) < 1) {
/* free previous */
if (*list) free(*list);
*list = 0;
return 0;
}
if ((array = (NETSTAT **)malloc(cnt * sizeof(NETSTAT *))) == 0)
return -1;
if (ph->ns_mutex) pthread_mutex_lock(ph->ns_mutex);
ht = ph->ns_hash;
/* reset total statistics */
ph->pkt_cnt = 0;
ph->pkt_len = 0;
ph->data_len = 0;
ph->pkt_cnt_rate = 0;
ph->pkt_len_rate = 0;
ph->data_len_rate = 0;
op = hfirst(ht);
i = 0;
while (op && i < cnt) {
ns = hstuff(ht);
if (ns) {
array[i++] = ns;
/* collect total statistics */
ph->pkt_cnt += ns->pkt_cnt;
ph->pkt_len += ns->pkt_len;
ph->data_len += ns->data_len;
ph->pkt_cnt_rate += ns->pkt_cnt_rate;
ph->pkt_len_rate += ns->pkt_len_rate;
ph->data_len_rate += ns->data_len_rate;
}
op = hnext(ht);
}
/* free previous */
if (*list) free(*list);
*list = array;
if (ph->ns_mutex) pthread_mutex_unlock(ph->ns_mutex);
return i;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>