Annotation of embedaddon/iftop/iftop.c, revision 1.1.1.1
1.1 misho 1: /*
2: * iftop.c:
3: *
4: */
5:
6: #include "integers.h"
7:
8: #if defined(HAVE_PCAP_H)
9: # include <pcap.h>
10: #elif defined(HAVE_PCAP_PCAP_H)
11: # include <pcap/pcap.h>
12: #else
13: # error No pcap.h
14: #endif
15: #include <stdio.h>
16: #include <stdlib.h>
17: #include <time.h>
18: #include <sys/types.h>
19: #include <sys/ioctl.h>
20: #include <sys/socket.h>
21: #include <net/if.h>
22:
23: #include <pthread.h>
24: #include <curses.h>
25: #include <signal.h>
26: #include <string.h>
27: #include <unistd.h>
28:
29: #include "iftop.h"
30: #include "addr_hash.h"
31: #include "resolver.h"
32: #include "ui.h"
33: #include "options.h"
34: #ifdef DLT_LINUX_SLL
35: #include "sll.h"
36: #endif /* DLT_LINUX_SLL */
37: #include "threadprof.h"
38: #include "ether.h"
39: #include "ip.h"
40: #include "tcp.h"
41: #include "token.h"
42: #include "llc.h"
43: #include "extract.h"
44: #include "ethertype.h"
45: #include "cfgfile.h"
46: #include "ppp.h"
47:
48:
49: /* ethernet address of interface. */
50: int have_hw_addr = 0;
51: unsigned char if_hw_addr[6];
52:
53: /* IP address of interface */
54: int have_ip_addr = 0;
55: struct in_addr if_ip_addr;
56:
57: extern options_t options;
58:
59: hash_type* history;
60: history_type history_totals;
61: time_t last_timestamp;
62: int history_pos = 0;
63: int history_len = 1;
64: pthread_mutex_t tick_mutex;
65:
66: pcap_t* pd; /* pcap descriptor */
67: struct bpf_program pcap_filter;
68: pcap_handler packet_handler;
69:
70: sig_atomic_t foad;
71:
72: static void finish(int sig) {
73: foad = sig;
74: }
75:
76:
77:
78:
79: /* Only need ethernet (plus optional 4 byte VLAN) and IP headers (48) + first 2 bytes of tcp/udp header */
80: #define CAPTURE_LENGTH 72
81:
82: void init_history() {
83: history = addr_hash_create();
84: last_timestamp = time(NULL);
85: memset(&history_totals, 0, sizeof history_totals);
86: }
87:
88: history_type* history_create() {
89: history_type* h;
90: h = xcalloc(1, sizeof *h);
91: return h;
92: }
93:
94: void history_rotate() {
95: hash_node_type* n = NULL;
96: history_pos = (history_pos + 1) % HISTORY_LENGTH;
97: hash_next_item(history, &n);
98: while(n != NULL) {
99: hash_node_type* next = n;
100: history_type* d = (history_type*)n->rec;
101: hash_next_item(history, &next);
102:
103: if(d->last_write == history_pos) {
104: addr_pair key = *(addr_pair*)(n->key);
105: hash_delete(history, &key);
106: free(d);
107: }
108: else {
109: d->recv[history_pos] = 0;
110: d->sent[history_pos] = 0;
111: }
112: n = next;
113: }
114:
115: history_totals.sent[history_pos] = 0;
116: history_totals.recv[history_pos] = 0;
117:
118: if(history_len < HISTORY_LENGTH) {
119: history_len++;
120: }
121: }
122:
123:
124: void tick(int print) {
125: time_t t;
126:
127: pthread_mutex_lock(&tick_mutex);
128:
129: t = time(NULL);
130: if(t - last_timestamp >= RESOLUTION) {
131: //printf("TICKING\n");
132: analyse_data();
133: ui_print();
134: history_rotate();
135: last_timestamp = t;
136: }
137: else {
138: ui_tick(print);
139: }
140:
141: pthread_mutex_unlock(&tick_mutex);
142: }
143:
144: int in_filter_net(struct in_addr addr) {
145: int ret;
146: ret = ((addr.s_addr & options.netfiltermask.s_addr) == options.netfilternet.s_addr);
147: return ret;
148: }
149:
150: int ip_addr_match(struct in_addr addr) {
151: return addr.s_addr == if_ip_addr.s_addr;
152: }
153:
154: /**
155: * Creates an addr_pair from an ip (and tcp/udp) header, swapping src and dst
156: * if required
157: */
158: void assign_addr_pair(addr_pair* ap, struct ip* iptr, int flip) {
159: unsigned short int src_port = 0;
160: unsigned short int dst_port = 0;
161:
162: /* Does this protocol use ports? */
163: if(iptr->ip_p == IPPROTO_TCP || iptr->ip_p == IPPROTO_UDP) {
164: /* We take a slight liberty here by treating UDP the same as TCP */
165:
166: /* Find the TCP/UDP header */
167: struct tcphdr* thdr = ((void*)iptr) + IP_HL(iptr) * 4;
168: src_port = ntohs(thdr->th_sport);
169: dst_port = ntohs(thdr->th_dport);
170: }
171:
172: if(flip == 0) {
173: ap->src = iptr->ip_src;
174: ap->src_port = src_port;
175: ap->dst = iptr->ip_dst;
176: ap->dst_port = dst_port;
177: }
178: else {
179: ap->src = iptr->ip_dst;
180: ap->src_port = dst_port;
181: ap->dst = iptr->ip_src;
182: ap->dst_port = src_port;
183: }
184:
185: }
186:
187: static void handle_ip_packet(struct ip* iptr, int hw_dir)
188: {
189: int direction = 0; /* incoming */
190: history_type* ht;
191: union {
192: history_type **ht_pp;
193: void **void_pp;
194: } u_ht = { &ht };
195: addr_pair ap;
196: int len;
197:
198: if(options.netfilter == 0) {
199: /*
200: * Net filter is off, so assign direction based on MAC address
201: */
202: if(hw_dir == 1) {
203: /* Packet leaving this interface. */
204: assign_addr_pair(&ap, iptr, 0);
205: direction = 1;
206: }
207: else if(hw_dir == 0) {
208: /* Packet incoming */
209: assign_addr_pair(&ap, iptr, 1);
210: direction = 0;
211: }
212: /* Packet direction is not given away by h/ware layer. Try IP
213: * layer
214: */
215: else if(have_ip_addr && ip_addr_match(iptr->ip_src)) {
216: /* outgoing */
217: assign_addr_pair(&ap, iptr, 0);
218: direction = 1;
219: }
220: else if(have_ip_addr && ip_addr_match(iptr->ip_dst)) {
221: /* incoming */
222: assign_addr_pair(&ap, iptr, 1);
223: direction = 0;
224: }
225: /*
226: * Cannot determine direction from hardware or IP levels. Therefore
227: * assume that it was a packet between two other machines, assign
228: * source and dest arbitrarily (by numerical value) and account as
229: * incoming.
230: */
231: else if (options.promiscuous_but_choosy) {
232: return; /* junk it */
233: }
234: else if(iptr->ip_src.s_addr < iptr->ip_dst.s_addr) {
235: assign_addr_pair(&ap, iptr, 1);
236: direction = 0;
237: }
238: else {
239: assign_addr_pair(&ap, iptr, 0);
240: direction = 0;
241: }
242: }
243: else {
244: /*
245: * Net filter on, assign direction according to netmask
246: */
247: if(in_filter_net(iptr->ip_src) && !in_filter_net(iptr->ip_dst)) {
248: /* out of network */
249: assign_addr_pair(&ap, iptr, 0);
250: direction = 1;
251: }
252: else if(in_filter_net(iptr->ip_dst) && !in_filter_net(iptr->ip_src)) {
253: /* into network */
254: assign_addr_pair(&ap, iptr, 1);
255: direction = 0;
256: }
257: else {
258: /* drop packet */
259: return ;
260: }
261: }
262:
263: ap.protocol = iptr->ip_p;
264:
265: /* Add the addresses to be resolved */
266: resolve(&iptr->ip_dst, NULL, 0);
267: resolve(&iptr->ip_src, NULL, 0);
268:
269: if(hash_find(history, &ap, u_ht.void_pp) == HASH_STATUS_KEY_NOT_FOUND) {
270: ht = history_create();
271: hash_insert(history, &ap, ht);
272: }
273:
274: len = ntohs(iptr->ip_len);
275:
276: /* Update record */
277: ht->last_write = history_pos;
278: if(iptr->ip_src.s_addr == ap.src.s_addr) {
279: ht->sent[history_pos] += len;
280: ht->total_sent += len;
281: }
282: else {
283: ht->recv[history_pos] += len;
284: ht->total_recv += len;
285: }
286:
287: if(direction == 0) {
288: /* incoming */
289: history_totals.recv[history_pos] += len;
290: history_totals.total_recv += len;
291: }
292: else {
293: history_totals.sent[history_pos] += len;
294: history_totals.total_sent += len;
295: }
296:
297: }
298:
299: static void handle_raw_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
300: {
301: handle_ip_packet((struct ip*)packet, -1);
302: }
303:
304: static void handle_llc_packet(const struct llc* llc, int dir) {
305:
306: struct ip* ip = (struct ip*)((void*)llc + sizeof(struct llc));
307:
308: /* Taken from tcpdump/print-llc.c */
309: if(llc->ssap == LLCSAP_SNAP && llc->dsap == LLCSAP_SNAP
310: && llc->llcui == LLC_UI) {
311: u_int32_t orgcode;
312: register u_short et;
313: orgcode = EXTRACT_24BITS(&llc->llc_orgcode[0]);
314: et = EXTRACT_16BITS(&llc->llc_ethertype[0]);
315: switch(orgcode) {
316: case OUI_ENCAP_ETHER:
317: case OUI_CISCO_90:
318: handle_ip_packet(ip, dir);
319: break;
320: case OUI_APPLETALK:
321: if(et == ETHERTYPE_ATALK) {
322: handle_ip_packet(ip, dir);
323: }
324: break;
325: default:;
326: /* Not a lot we can do */
327: }
328: }
329: }
330:
331: static void handle_tokenring_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
332: {
333: struct token_header *trp;
334: int dir = -1;
335: trp = (struct token_header *)packet;
336:
337: if(IS_SOURCE_ROUTED(trp)) {
338: packet += RIF_LENGTH(trp);
339: }
340: packet += TOKEN_HDRLEN;
341:
342: if(memcmp(trp->token_shost, if_hw_addr, 6) == 0 ) {
343: /* packet leaving this i/f */
344: dir = 1;
345: }
346: else if(memcmp(trp->token_dhost, if_hw_addr, 6) == 0 || memcmp("\xFF\xFF\xFF\xFF\xFF\xFF", trp->token_dhost, 6) == 0) {
347: /* packet entering this i/f */
348: dir = 0;
349: }
350:
351: /* Only know how to deal with LLC encapsulated packets */
352: if(FRAME_TYPE(trp) == TOKEN_FC_LLC) {
353: handle_llc_packet((struct llc*)packet, dir);
354: }
355: }
356:
357: static void handle_ppp_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
358: {
359: register u_int length = pkthdr->len;
360: register u_int caplen = pkthdr->caplen;
361: u_int proto;
362:
363: if (caplen < 2)
364: return;
365:
366: if(packet[0] == PPP_ADDRESS) {
367: if (caplen < 4)
368: return;
369:
370: packet += 2;
371: length -= 2;
372:
373: proto = EXTRACT_16BITS(packet);
374: packet += 2;
375: length -= 2;
376:
377: if(proto == PPP_IP || proto == ETHERTYPE_IP) {
378: handle_ip_packet((struct ip*)packet, -1);
379: }
380: }
381: }
382:
383: #ifdef DLT_LINUX_SLL
384: static void handle_cooked_packet(unsigned char *args, const struct pcap_pkthdr * thdr, const unsigned char * packet)
385: {
386: struct sll_header *sptr;
387: int dir = -1;
388: sptr = (struct sll_header *) packet;
389:
390: switch (ntohs(sptr->sll_pkttype))
391: {
392: case LINUX_SLL_HOST:
393: /*entering this interface*/
394: dir = 0;
395: break;
396: case LINUX_SLL_OUTGOING:
397: /*leaving this interface */
398: dir=1;
399: break;
400: }
401: handle_ip_packet((struct ip*)(packet+SLL_HDR_LEN), dir);
402: }
403: #endif /* DLT_LINUX_SLL */
404:
405: static void handle_eth_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
406: {
407: struct ether_header *eptr;
408: int ether_type;
409: const unsigned char *payload;
410: eptr = (struct ether_header*)packet;
411: ether_type = ntohs(eptr->ether_type);
412: payload = packet + sizeof(struct ether_header);
413:
414: tick(0);
415:
416: if(ether_type == ETHERTYPE_8021Q) {
417: struct vlan_8021q_header* vptr;
418: vptr = (struct vlan_8021q_header*)payload;
419: ether_type = ntohs(vptr->ether_type);
420: payload += sizeof(struct vlan_8021q_header);
421: }
422:
423: if(ether_type == ETHERTYPE_IP) {
424: struct ip* iptr;
425: int dir = -1;
426:
427: /*
428: * Is a direction implied by the MAC addresses?
429: */
430: if(have_hw_addr && memcmp(eptr->ether_shost, if_hw_addr, 6) == 0 ) {
431: /* packet leaving this i/f */
432: dir = 1;
433: }
434: else if(have_hw_addr && memcmp(eptr->ether_dhost, if_hw_addr, 6) == 0 ) {
435: /* packet entering this i/f */
436: dir = 0;
437: }
438: else if (memcmp("\xFF\xFF\xFF\xFF\xFF\xFF", eptr->ether_dhost, 6) == 0) {
439: /* broadcast packet, count as incoming */
440: dir = 0;
441: }
442:
443: iptr = (struct ip*)(payload); /* alignment? */
444: handle_ip_packet(iptr, dir);
445: }
446: }
447:
448:
449: /* set_filter_code:
450: * Install some filter code. Returns NULL on success or an error message on
451: * failure. */
452: char *set_filter_code(const char *filter) {
453: char *x;
454: if (filter) {
455: x = xmalloc(strlen(filter) + sizeof "() and ip");
456: sprintf(x, "(%s) and ip", filter);
457: } else
458: x = xstrdup("ip");
459: if (pcap_compile(pd, &pcap_filter, x, 1, 0) == -1) {
460: xfree(x);
461: return pcap_geterr(pd);
462: }
463: xfree(x);
464: if (pcap_setfilter(pd, &pcap_filter) == -1)
465: return pcap_geterr(pd);
466: else
467: return NULL;
468: }
469:
470:
471:
472: /*
473: * packet_init:
474: *
475: * performs pcap initialisation, called before ui is initialised
476: */
477: void packet_init() {
478: char errbuf[PCAP_ERRBUF_SIZE];
479: char *m;
480: int s;
481: int i;
482: int dlt;
483: int result;
484:
485: #ifdef HAVE_DLPI
486: result = get_addrs_dlpi(options.interface, if_hw_addr, &if_ip_addr);
487: #else
488: result = get_addrs_ioctl(options.interface, if_hw_addr, &if_ip_addr);
489: #endif
490:
491: if (result < 0) {
492: exit(1);
493: }
494:
495: have_hw_addr = result & 1;
496: have_ip_addr = result & 2;
497:
498: if(have_ip_addr) {
499: fprintf(stderr, "IP address is: %s\n", inet_ntoa(if_ip_addr));
500: }
501:
502: if(have_hw_addr) {
503: fprintf(stderr, "MAC address is:");
504: for (i = 0; i < 6; ++i)
505: fprintf(stderr, "%c%02x", i ? ':' : ' ', (unsigned int)if_hw_addr[i]);
506: fprintf(stderr, "\n");
507: }
508:
509: // exit(0);
510: resolver_initialise();
511:
512: pd = pcap_open_live(options.interface, CAPTURE_LENGTH, options.promiscuous, 1000, errbuf);
513: // DEBUG: pd = pcap_open_offline("tcpdump.out", errbuf);
514: if(pd == NULL) {
515: fprintf(stderr, "pcap_open_live(%s): %s\n", options.interface, errbuf);
516: exit(1);
517: }
518: dlt = pcap_datalink(pd);
519: if(dlt == DLT_EN10MB) {
520: packet_handler = handle_eth_packet;
521: }
522: else if(dlt == DLT_RAW || dlt == DLT_NULL) {
523: packet_handler = handle_raw_packet;
524: }
525: else if(dlt == DLT_IEEE802) {
526: packet_handler = handle_tokenring_packet;
527: }
528: else if(dlt == DLT_PPP) {
529: packet_handler = handle_ppp_packet;
530: }
531: /*
532: * SLL support not available in older libpcaps
533: */
534: #ifdef DLT_LINUX_SLL
535: else if(dlt == DLT_LINUX_SLL) {
536: packet_handler = handle_cooked_packet;
537: }
538: #endif
539: else {
540: fprintf(stderr, "Unsupported datalink type: %d\n"
541: "Please email pdw@ex-parrot.com, quoting the datalink type and what you were\n"
542: "trying to do at the time\n.", dlt);
543: exit(1);
544: }
545:
546: if ((m = set_filter_code(options.filtercode))) {
547: fprintf(stderr, "set_filter_code: %s\n", m);
548: exit(1);
549: return;
550: }
551: }
552:
553: /* packet_loop:
554: * Worker function for packet capture thread. */
555: void packet_loop(void* ptr) {
556: pcap_loop(pd,-1,(pcap_handler)packet_handler,NULL);
557: }
558:
559:
560: /* main:
561: * Entry point. See usage(). */
562: int main(int argc, char **argv) {
563: pthread_t thread;
564: struct sigaction sa = {};
565:
566: /* TODO: tidy this up */
567: /* read command line options and config file */
568: config_init();
569: options_set_defaults();
570: options_read_args(argc, argv);
571: /* If a config was explicitly specified, whinge if it can't be found */
572: read_config(options.config_file, options.config_file_specified);
573: options_make();
574:
575: sa.sa_handler = finish;
576: sigaction(SIGINT, &sa, NULL);
577:
578: pthread_mutex_init(&tick_mutex, NULL);
579:
580: packet_init();
581:
582: init_history();
583:
584: ui_init();
585:
586: pthread_create(&thread, NULL, (void*)&packet_loop, NULL);
587:
588: ui_loop();
589:
590: pthread_cancel(thread);
591:
592: ui_finish();
593:
594: return 0;
595: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>