/* * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #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; }