/*
* Copyright 2001 The Regents of the University of California
* All Rights Reserved
*
* Permission to use, copy, modify and distribute any part of this
* iffinder software package for educational, research and non-profit
* purposes, without fee, and without a written agreement is hereby
* granted, provided that the above copyright notice, this paragraph
* and the following paragraphs appear in all copies.
*
* Those desiring to incorporate this into commercial products or use
* for commercial purposes should contact the Technology Transfer
* Office, University of California, San Diego, 9500 Gilman Drive, La
* Jolla, CA 92093-0910, Ph: (858) 534-5815, FAX: (858) 534-7345.
*
* IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY
* PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
* DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
* THE SOFTWARE PROVIDED HEREIN IS ON AN "AS IS" BASIS, AND THE
* UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
* SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. THE UNIVERSITY
* OF CALIFORNIA MAKES NO REPRESENTATIONS AND EXTENDS NO WARRANTIES
* OF ANY KIND, EITHER IMPLIED OR EXPRESS, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A
* PARTICULAR PURPOSE, OR THAT THE USE OF THE SOFTWARE WILL NOT INFRINGE
* ANY PATENT, TRADEMARK OR OTHER RIGHTS.
*
* The iffinder software package is developed by Ken Keys at the
* University of California, San Diego under the Cooperative Association
* for Internet Data Analysis (CAIDA) Program.
*/
/* Note: this only counts interfaces in the first column of input.
* iffinder includes all new interfaces in the first column, so this works
* the same as if it counted all interfaces. But if you strip new interface
* lines from iffinder's output, you can use this to count only previously
* known (skitter) interfaces.
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
/*#include <netinet/in_systm.h>*/
/*#include <netinet/ip.h>*/
#include <arpa/inet.h>
#include "hashtab.h"
#define INTERFACE_TABLE_SIZE 25013
#define HOST_TABLE_SIZE 10007
static int verbosity = 0;
typedef struct interface {
struct in_addr addr; /* IP address of interface */
struct interface *node; /* node to which this interface belongs */
struct interface *next, *last; /* next/last interface in node's list */
int tcount; /* # of ifaces on this node */
int ocount; /* # of ifaces on this node found by iffinder */
char discover_how; /* this interface was found by iffinder */
} interface_t;
static const char *argv0;
static hash_tab *interface_table;
typedef struct {
int ifaces;
int nodes; /* includes single ifaces */
int multinodes; /* nodes with more than 1 interface */
int multiifaces; /* interfaces on nodes with more than 1 interface */
} stats_t;
static stats_t tstats, ostats;
static int eprintf(const char *fmt, ...)
{
va_list ap;
int result, err;
err = errno;
va_start(ap, fmt);
result = vfprintf(stderr, fmt, ap);
va_end(ap);
errno = err;
return result;
}
#define diag(level, args) \
do { if (level <= verbosity) { eprintf args; } } while (0)
/*
* functions for interface hash table
*/
static int cmp_if(const void *a, const void *b)
{
return ((interface_t*)a)->addr.s_addr != ((interface_t*)b)->addr.s_addr;
}
static unsigned long key_if(const void *a)
{
return ((interface_t*)a)->addr.s_addr;
}
static void sanity_check(interface_t *iface)
{
if (iface->node == iface) {
assert(iface->tcount > 0);
assert(iface->ocount >= 0);
assert((iface->tcount == 1) == !iface->next);
assert((iface->tcount == 1) == !iface->last);
assert(iface->tcount <= tstats.ifaces);
assert(iface->ocount <= ostats.ifaces);
} else {
assert(iface->tcount == 0);
assert(iface->ocount == 0);
assert(!iface->last);
}
assert(ostats.multiifaces >= 0);
assert(tstats.multiifaces >= 0);
assert(ostats.multiifaces >= 2 * ostats.multinodes);
assert(tstats.multiifaces >= 2 * tstats.multinodes);
assert(ostats.multiifaces <= ostats.ifaces);
assert(tstats.multiifaces <= tstats.ifaces);
}
static void merge_nodes(interface_t *node1, interface_t *node2)
{
interface_t *iface;
int tcount, ocount;
if (node1 == node2)
return;
sanity_check(node1);
sanity_check(node2);
ocount = node1->ocount + node2->ocount;
tcount = node1->tcount + node2->tcount;
ostats.multinodes += (ocount >1) - (node1->ocount >1) - (node2->ocount >1);
tstats.multinodes += (tcount >1) - (node1->tcount >1) - (node2->tcount >1);
ostats.multiifaces += ocount > 1 ? ocount : 0
- (node1->ocount > 1) ? node1->ocount : 0
- (node2->ocount > 1) ? node2->ocount : 0;
assert(ostats.multiifaces >= 0);
tstats.multiifaces += tcount > 1 ? tcount : 0
- (node1->tcount > 1) ? node1->tcount : 0
- (node2->tcount > 1) ? node2->tcount : 0;
assert(tstats.multiifaces >= 0);
if (node1->ocount > 0 && node2->ocount > 0) ostats.nodes--;
if (node1->tcount > 0 && node2->tcount > 0) tstats.nodes--;
if (node1->tcount < node2->tcount) {
iface = node1; iface = node2; node2 = iface;
}
for (iface = node2; iface; iface = iface->next) {
assert(iface->node == node2);
iface->node = node1;
}
*(node1->last ? &node1->last->next : &node1->next) = node2;
node1->last = node2->last ? node2->last : node2;
node2->last = NULL;
node1->ocount = ocount;
node1->tcount = tcount;
node2->ocount = 0;
node2->tcount = 0;
sanity_check(node1);
sanity_check(node2);
}
static interface_t *new_interface(struct in_addr addr, char discover_how)
{
interface_t *interface;
interface = malloc(sizeof(interface_t));
if (!interface) return NULL;
interface->addr = addr;
interface->node = interface;
interface->next = NULL;
interface->last = NULL;
interface->tcount = 1;
interface->ocount = !discover_how;
interface->discover_how = discover_how;
ostats.nodes += !discover_how;
tstats.nodes += 1;
ostats.ifaces += !discover_how;
tstats.ifaces += 1;
add_hash_entry(interface_table, interface);
return interface;
}
static interface_t *find_interface(struct in_addr addr)
{
interface_t match;
match.addr = addr;
return find_hash_entry(interface_table, &match);
}
static interface_t *find_or_create_interface(struct in_addr addr, char discover_how,
const char *filename, int linenum)
{
interface_t *iface;
if ((iface = find_interface(addr))) {
if (!discover_how && iface->discover_how) {
iface->discover_how = 0;
ostats.ifaces++;
if (iface->ocount == 1) {
ostats.multiifaces++;
ostats.multinodes++;
}
ostats.multiifaces++;
iface->ocount++;
}
} else if (!(iface = new_interface(addr, discover_how))) {
fprintf(stderr, "%s: %s, line %d: %s\n",
argv0, filename, linenum, strerror(errno));
}
return iface;
}
static int read_ip_file(const char *filename)
{
FILE *file;
char line[80], *p, *token;
int linenum = 0, result = 0;
char discover_how;
struct in_addr addr, alias_id;
interface_t *iface;
if (!(file = fopen(filename, "r"))) {
fprintf(stderr, "%s: %s: %s\n", argv0, filename, strerror(errno));
return 0;
}
while (fgets(line, sizeof(line), file)) {
linenum++;
if (line[0] == '#') /* comment */
continue;
p = line;
token = strsep(&p, " \t\n");
if (!*token) /* empty line */
continue;
if (!inet_aton(token, &addr)) {
fprintf(stderr, "%s: %s, line %d: bad IP address '%s'\n",
argv0, filename, linenum, token);
goto error;
}
while ((token = strsep(&p, " \t\n")) && !*token);
if (token && isdigit(*token)) {
if (!inet_aton(token, &alias_id)) {
fprintf(stderr, "%s: %s, line %d: bad node id '%s'\n",
argv0, filename, linenum, token);
goto error;
}
} else {
alias_id.s_addr = 0;
}
discover_how = 0;
if (token) while ((token = strsep(&p, " \t\n")) && !*token);
if (token) while ((token = strsep(&p, " \t\n")) && !*token);
if (token) while ((token = strsep(&p, " \t\n")) && !*token);
if (token) {
discover_how = (*token == '-') ? 0 : *token;
}
iface = find_or_create_interface(addr, discover_how, filename, linenum);
if (!iface) goto error;
if (alias_id.s_addr) {
interface_t *alias;
alias = find_or_create_interface(alias_id, 1, filename, linenum);
if (!alias) goto error;
merge_nodes(alias->node, iface->node);
}
}
result = 1;
fclose(file);
return result;
error:
exit(1);
}
static void dump_iface(interface_t *iface)
{
printf("%-15s ", inet_ntoa(iface->addr));
printf("%-15s ", iface->node->tcount > 1 ?
inet_ntoa(iface->node->addr) : "-");
printf("%c", iface->discover_how ? iface->discover_how : '-');
putchar('\n');
}
static void dump_table(void)
{
interface_t *node, *iface;
printf("# %-13s %-15s %s\n", "address", "node_id", "discovr");
init_hash_walk(interface_table);
while ((node = next_hash_walk(interface_table))) {
if (node->tcount <= 1) continue;
assert (node->node == node);
for (iface = node; iface; iface = iface->next) {
assert(iface->node == node);
dump_iface(iface);
}
}
init_hash_walk(interface_table);
while ((iface = next_hash_walk(interface_table))) {
if (iface->node->tcount > 1) continue;
assert(iface->node->tcount > 0);
dump_iface(iface);
}
}
static void usage_exit(const char *msg)
{
if (msg) eprintf("%s\n", msg);
eprintf("Usage: %s [options] <ip_file>\n",
argv0);
eprintf("Options (with defaults in brackets):\n");
eprintf("-v<V> verbosity <V> [1]\n");
exit(1);
}
static void dump_stats(const char *label, stats_t *s)
{
printf("# Statistics for %s interfaces\n", label);
printf("# interfaces: %8d\n", s->ifaces);
printf("# on nodes w/ >1 iface: %8d\n", s->multiifaces);
printf("# single: %8d\n", s->ifaces - s->multiifaces);
printf("# nodes: %8d\n", s->nodes);
printf("# with >1 iface: %8d\n", s->multinodes);
}
int main(int argc, char *argv[])
{
int opt;
argv0 = argv[0];
/* command line options */
while ((opt = getopt(argc, argv, "v:")) != -1) {
switch (opt) {
case 'v':
verbosity = atoi(optarg);
break;
default:
usage_exit(NULL);
}
}
if (optind >= argc) {
usage_exit(NULL);
}
interface_table = init_hash_table("interface table", cmp_if, key_if,
NULL, INTERFACE_TABLE_SIZE);
while (optind < argc)
read_ip_file(argv[optind++]);
printf("# ifclosure revision: %s\n", "$Revision: 1.5 $");
printf("#\n");
dump_stats("original", &ostats);
printf("#\n");
dump_stats("all", &tstats);
printf("\n");
dump_table();
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1