Annotation of embedaddon/trafshow/show_dump.c, revision 1.1.1.1.2.1
1.1 misho 1: /*
2: * Copyright (c) 2004 Rinet Corp., Novosibirsk, Russia
3: *
4: * Redistribution and use in source forms, with and without modification,
5: * are permitted provided that this entire comment appears intact.
6: *
7: * THIS SOURCE CODE IS PROVIDED ``AS IS'' WITHOUT ANY WARRANTIES OF ANY KIND.
8: */
9:
10: #ifdef HAVE_CONFIG_H
11: #include <config.h>
12: #endif
13:
14: #ifdef HAVE_SLCURSES
15: #include <slcurses.h>
16: #elif HAVE_NCURSES
17: #include <ncurses.h>
18: #else
19: #include <curses.h>
20: #endif
21: #include <sys/param.h>
22: #include <sys/types.h>
23: #include <sys/socket.h>
1.1.1.1.2.1! misho 24: #include <sys/ioctl.h>
1.1 misho 25: #include <sys/time.h>
26: #include <netinet/in.h>
27: #include <arpa/inet.h>
28: #include <stdio.h>
29: #include <stdlib.h>
30: #include <string.h>
31: #include <unistd.h>
32: #include <errno.h>
1.1.1.1.2.1! misho 33: #include <net/bpf.h>
1.1 misho 34: #include <pcap.h>
35: #include <pthread.h>
36: #include <time.h>
37: #include <ctype.h>
38:
39: #include "show_dump.h"
40: #include "show_stat.h" /* just for hdr2str() */
41: #include "parse_dl.h"
42: #include "trafshow.h"
43: #include "screen.h"
44: #include "netstat.h"
45: #include "getkey.h"
46: #include "addrtoname.h"
47: #include "util.h"
48:
49: NETSTAT *dump_match = 0;
50: const char *cisco_netflow_dump = 0;
51: const char *dump_file = 0;
52:
53: static char *build_filter_expr(char *dst, int size, const NETSTAT *ns);
54: static void *live_pcap_dump();
55: static void live_pcap_parse(u_char *a, const struct pcap_pkthdr *h, const u_char *p);
56: static void file_pcap_parse(u_char *a, const struct pcap_pkthdr *h, const u_char *p);
57: static void show_header_dump(PCAP_HANDLER *ph, const NETSTAT *ns);
58: static void show_ascii_dump(const u_char *p, int length);
59: static void show_hex_dump(const u_char *p, int length);
60:
61: static pcap_t *live_pcap = 0;
62: static pcap_dumper_t *live_dump = 0;
63: static pthread_t *live_pcap_thr = 0;
64: static pcap_t *file_pcap = 0;
65: static FILE *file_netflow = 0;
66: static int redraw_lines = 0;
67:
68: static void
69: print_mode(void)
70: {
71: const char *cp = cisco_netflow_dump;
72: char src_buf[100], dst_buf[100], proto_buf[20];
73:
74: /* sanity check */
75: if (!dump_match) return;
76:
77: hdr2str(&dump_match->ns_hdr,
78: src_buf, sizeof(src_buf),
79: dst_buf, sizeof(dst_buf),
80: proto_buf, sizeof(proto_buf));
81:
82: if (!cisco_netflow_dump) {
83: switch (show_stat_mode) {
84: case Size: cp = "HexData"; break;
85: case Data: cp = "AsciiData"; break;
86: case Packets: cp = "Packets"; break;
87: }
88: }
89:
90: attrset(A_STANDOUT);
91: printw("\n--- %s %s > %s %s flow ---",
92: proto_buf, src_buf, dst_buf, cp);
93: attrset(A_NORMAL);
94:
95: #ifdef HAVE_WREDRAWLN
96: wredrawln(stdscr, 0, LINES);
97: #endif
98: refresh();
99: }
100:
101: #ifndef HAVE_PCAP_DUMP_FLUSH
102: int
103: pcap_dump_flush(pcap_dumper_t *p)
104: {
105:
106: if (fflush((FILE *)p) == EOF)
107: return (-1);
108: else
109: return (0);
110: }
111: #endif
112:
113: int
114: show_dump_open(ph, ns)
115: const PCAP_HANDLER *ph;
116: const NETSTAT *ns;
117: {
118: int op;
119: struct bpf_program filter;
120: bpf_u_int32 net;
121: bpf_u_int32 mask;
122: char name[100], buf[256];
1.1.1.1.2.1! misho 123: int v = 1;
1.1 misho 124:
125: /* sanity check */
126: if (!ph || !ns) return -1;
127:
128: show_dump_close();
129:
130: if (!dump_match && (dump_match = (NETSTAT *)malloc(sizeof(NETSTAT))) == 0) {
131: screen_status("%s: malloc: Out of memory?", ph->name);
132: show_dump_close();
133: return -1;
134: }
135: memcpy(dump_match, ns, sizeof(NETSTAT));
136:
137: if (ph->pcap) {
138: /* open live packet capture */
139: buf[0] = '\0';
140: live_pcap = pcap_open_live(strcpy(name, ph->name),
141: DUMP_SNAPLEN, promisc, 1, buf);
142: if (buf[0] != '\0')
143: screen_status("%s: %s", ph->name, buf);
144: if (!live_pcap) return -1;
145: if (pcap_setnonblock(live_pcap, 1, buf) < 0) {
146: screen_status("%s: %s", ph->name, buf);
147: show_dump_close();
148: return -1;
149: }
1.1.1.1.2.1! misho 150: if (ioctl(pcap_fileno(live_pcap), BIOCIMMEDIATE, &v) < 0) {
! 151: screen_status("%s: %s", ph->name, strerror(errno));
! 152: show_dump_close();
! 153: return -1;
! 154: }
1.1 misho 155: /* setup filter expression */
156: if (pcap_lookupnet(strcpy(name, ph->name), &net, &mask, buf) < 0) {
157: /* ignore error */
158: net = 0;
159: mask = 0;
160: }
161: if (!build_filter_expr(buf, sizeof(buf), ns)) {
162: screen_status("%s: Can't build filter expression", ph->name);
163: show_dump_close();
164: return -1;
165: }
166: if (pcap_compile(live_pcap, &filter, buf, Oflag, mask) < 0) {
167: screen_status("%s: %s", ph->name, pcap_geterr(live_pcap));
168: show_dump_close();
169: return -1;
170: }
171: op = pcap_setfilter(live_pcap, &filter);
172: pcap_freecode(&filter);
173: if (op < 0) {
174: screen_status("%s: %s", ph->name, pcap_geterr(live_pcap));
175: show_dump_close();
176: return -1;
177: }
178: } else if ((cisco_netflow_dump = strdup(ph->name)) == 0) {
179: screen_status("%s: strdup: Out of memory?", ph->name);
180: show_dump_close();
181: return -1;
182: }
183:
184: /* open pcap dump file for writing */
185:
186: snprintf(buf, sizeof(buf), "%s/%s.XXXXXX", TEMP_DIR, progname);
187: if ((op = mkstemp(buf)) < 0) {
188: screen_status("%s: %s: %s",
189: ph->name, buf, strerror(errno));
190: show_dump_close();
191: return -1;
192: }
193: (void)close(op);
194: if ((dump_file = strdup(buf)) == 0) {
195: screen_status("%s: strdup: Out of memory?", ph->name);
196: show_dump_close();
197: return -1;
198: }
199:
200: if (!cisco_netflow_dump) {
201: if ((live_dump = pcap_dump_open(live_pcap, dump_file)) == 0) {
202: screen_status("%s: %s", ph->name, pcap_geterr(live_pcap));
203: show_dump_close();
204: return -1;
205: }
206: pcap_dump_flush(live_dump); /* write header right now */
207:
208: /* spawn thread to dump live packet capture into the file */
209: if ((live_pcap_thr = (pthread_t *)malloc(sizeof(pthread_t))) == 0) {
210: screen_status("%s: malloc: Out of memory?", ph->name);
211: show_dump_close();
212: return -1;
213: }
214: if (pthread_create(live_pcap_thr, 0, live_pcap_dump, 0)) {
215: screen_status("%s: pthread_create: Out of resources?", ph->name);
216: show_dump_close();
217: return -1;
218: }
219:
220: /* open pcap dump file for reading */
221: if ((file_pcap = pcap_open_offline(dump_file, buf)) == 0) {
222: screen_status("%s: %s", ph->name, buf);
223: show_dump_close();
224: return -1;
225: }
226: } else if ((file_netflow = fopen(dump_file, "r")) == 0) {
227: screen_status("%s: %s: %s",
228: ph->name, dump_file, strerror(errno));
229: show_dump_close();
230: return -1;
231: }
232:
233: scrollok(stdscr, 1);
234: screen_clear();
235: print_mode();
236: return 0;
237: }
238:
239: static void *
240: live_pcap_dump()
241: {
242: int op;
243:
244: while (live_pcap && live_dump) {
245: op = pcap_dispatch(live_pcap, -1, live_pcap_parse,
246: (u_char *)live_dump);
247: if (op == -2 || (op == -1 && errno != EAGAIN))
248: break;
249: if (op < 1) usleep(1000); /* 1ms idle to prevent deadloop */
250: }
251: return 0;
252: }
253:
254: static void
255: live_pcap_parse(a, h, p)
256: u_char *a;
257: const struct pcap_pkthdr *h;
258: const u_char *p;
259: {
260: NETSTAT ns;
261:
262: /* sanity check */
263: if (!a || !live_pcap) return;
264:
265: memset(&ns, 0, sizeof(NETSTAT));
266:
267: if (parse_dl(&ns, pcap_datalink(live_pcap), h->caplen, h->len, p) < 0)
268: return;
269:
270: if (!netstat_match(&ns, dump_match))
271: return;
272:
273: pcap_dump(a, h, p);
274: pcap_dump_flush((pcap_dumper_t *)a);
275: }
276:
277: void
278: show_dump_close()
279: {
280: if (cisco_netflow_dump) {
281: free((char *)cisco_netflow_dump);
282: cisco_netflow_dump = 0;
283: }
284: if (file_netflow) {
285: (void)fclose(file_netflow);
286: file_netflow = 0;
287: }
288:
289: if (live_pcap_thr) {
290: pthread_cancel(*live_pcap_thr);
291: free(live_pcap_thr);
292: live_pcap_thr = 0;
293: }
294: if (live_dump) {
295: pcap_dump_close(live_dump);
296: live_dump = 0;
297: }
298: if (live_pcap) {
299: pcap_close(live_pcap);
300: live_pcap = 0;
301: }
302: if (file_pcap) {
303: pcap_close(file_pcap);
304: file_pcap = 0;
305: }
306:
307: if (dump_file) {
308: (void)unlink(dump_file);
309: free((char *)dump_file);
310: dump_file = 0;
311: }
312: scrollok(stdscr, 0);
313: }
314:
315: void
316: show_dump_print(ph)
317: PCAP_HANDLER *ph;
318: {
319: if (!cisco_netflow_dump) {
320: int op;
321:
322: /* sanity check */
323: if (!file_pcap) return;
324:
325: clearerr(pcap_file(file_pcap)); /* tail file */
326: while ((op = pcap_dispatch(file_pcap, -1, file_pcap_parse,
327: (u_char *)ph)) > 0);
328: if (op < 0) {
329: if (op == -1)
330: screen_status(pcap_geterr(file_pcap));
331: return;
332: }
333: } else {
334: char *cp, buf[256];
335:
336: /* sanity check */
337: if (!file_netflow) return;
338:
339: clearerr(file_netflow); /* tail file */
340: while (fgets(buf, sizeof(buf), file_netflow) != 0) {
341: buf[sizeof(buf)-1] = '\0';
342: if ((cp = strpbrk(buf, "\r\n")) != '\0')
343: *cp = '\0';
344: printw("%s\n", buf);
345: redraw_lines++;
346: }
347: }
348: if (redraw_lines) {
349: #ifdef HAVE_WREDRAWLN
350: wredrawln(stdscr, 0, LINES);
351: #endif
352: refresh();
353: redraw_lines = 0;
354: }
355: }
356:
357: static void
358: file_pcap_parse(a, h, p)
359: u_char *a;
360: const struct pcap_pkthdr *h;
361: const u_char *p;
362: {
363: PCAP_HANDLER *ph = (PCAP_HANDLER *)a;
364: FILE *fp;
365: long sz;
366: int hdrlen;
367: NETSTAT ns;
368:
369: /* sanity check */
370: if (!file_pcap) return;
371:
372: /* prevent huge output */
373: if ((fp = pcap_file(file_pcap)) == 0 || (sz = fd_size(fileno(fp))) < 0)
374: return;
375: if (sz - ftell(fp) > DUMP_SNAPLEN * LINES)
376: return;
377:
378: memset(&ns, 0, sizeof(NETSTAT));
379:
380: hdrlen = parse_dl(&ns, pcap_datalink(file_pcap), h->caplen, h->len, p);
381: if (hdrlen < 0 || hdrlen > h->caplen)
382: return;
383:
384: if (!netstat_match(&ns, dump_match))
385: return;
386:
387: ns.mtime = h->ts;
388:
389: switch (show_stat_mode) {
390: case Size:
391: show_hex_dump(p + hdrlen, h->caplen - hdrlen);
392: break;
393: case Data:
394: show_ascii_dump(p + hdrlen, h->caplen - hdrlen);
395: break;
396: case Packets:
397: show_header_dump(ph, &ns);
398: break;
399: }
400: }
401:
402: void
403: show_dump_input(ch)
404: int ch;
405: {
406: if (ch == 'c' || ch == 'C' || ch == K_CTRL('R'))
407: screen_clear();
408: else if (show_stat_input(0, ch))
409: print_mode();
410: }
411:
412: static char *
413: build_filter_expr(dst, size, ns)
414: char *dst;
415: int size;
416: const NETSTAT *ns;
417: {
418: char src_addr[100], dst_addr[100];
419:
420: src_addr[0] = '\0';
421: dst_addr[0] = '\0';
422:
423: if (ns->ip_ver == 4) {
424: (void)strcpy(src_addr, intoa(ns->ip_src_addr.ip_addr.s_addr));
425: (void)strcpy(dst_addr, intoa(ns->ip_dst_addr.ip_addr.s_addr));
426: }
427: #ifdef INET6
428: else if (ns->ip_ver == 6) {
429: (void)inet_ntop(AF_INET6, &ns->ip_src_addr.ip6_addr, src_addr, sizeof(src_addr));
430: (void)inet_ntop(AF_INET6, &ns->ip_dst_addr.ip6_addr, dst_addr, sizeof(dst_addr));
431: }
432: #endif
433: else if (ns->eth_type) {
434: (void)strcpy(src_addr, etheraddr_string(ns->eth_src_addr));
435: (void)strcpy(dst_addr, etheraddr_string(ns->eth_dst_addr));
436: }
437:
438: if (src_addr[0] == '\0' || dst_addr[0] == '\0')
439: return 0; /* should not happen */
440:
441: if (ns->ip_ver) {
442: snprintf(dst, size,
443: "src %s and dst %s",
444: src_addr, dst_addr);
445: } else if (!strcmp(dst_addr, "broadcast") ||
446: !strcmp(dst_addr, "multicast")) {
447: snprintf(dst, size,
448: "ether src %s and ether %s",
449: src_addr, dst_addr);
450: } else {
451: snprintf(dst, size,
452: "ether src %s and ether dst %s",
453: src_addr, dst_addr);
454: }
455: return dst;
456: }
457:
458: static void
459: show_header_dump(ph, ns)
460: PCAP_HANDLER *ph;
461: const NETSTAT *ns;
462: {
463: char time_buf[100], src_buf[100], dst_buf[100], proto_buf[20];
464: #ifdef notdef
465: NETSTAT find = *ns;
466: if (netstat_find(ph, &find))
467: ns = &find;
468: #endif
469: (void)strftime(time_buf, sizeof(time_buf),
470: "%T", localtime((time_t *)&ns->mtime.tv_sec));
471: hdr2str(&ns->ns_hdr,
472: src_buf, sizeof(src_buf),
473: dst_buf, sizeof(dst_buf),
474: proto_buf, sizeof(proto_buf));
475:
476: printw("\n%s.%03d %s %s > %s %d/%d bytes",
477: time_buf, (int)(ns->mtime.tv_usec / 1000),
478: proto_buf, src_buf, dst_buf,
479: (int)ns->pkt_len, (int)ns->data_len);
480:
481: redraw_lines++;
482: }
483:
484: static void
485: show_ascii_dump(cp, length)
486: const u_char *cp;
487: int length;
488: {
489: /* sanity check */
490: if (!cp || length < 1)
491: return;
492:
493: if (!redraw_lines)
494: addch('\n');
495: while (--length >= 0) {
496: if (*cp != '\r' && *cp != '\b')
497: addch(*cp);
498: cp++;
499: }
500: redraw_lines++;
501: }
502:
503: #ifdef ACS_VLINE
504: #define VLINE ACS_VLINE
505: #else
506: #define VLINE '|'
507: #endif
508:
509: /* stolen from tcpdump's ascii-print() */
510:
511: #define HEXDUMP_BYTES_PER_LINE 16
512: #define HEXDUMP_SHORTS_PER_LINE (HEXDUMP_BYTES_PER_LINE / 2)
513: #define HEXDUMP_HEXSTUFF_PER_SHORT 5 /* 4 hex digits and a space */
514: #define HEXDUMP_HEXSTUFF_PER_LINE \
515: (HEXDUMP_HEXSTUFF_PER_SHORT * HEXDUMP_SHORTS_PER_LINE)
516:
517: static void
518: show_hex_dump(cp, length)
519: const u_char *cp;
520: int length;
521: {
522: int oset = 0;
523: register u_int i;
524: register int s1, s2;
525: register int nshorts;
526: char hexstuff[HEXDUMP_SHORTS_PER_LINE*HEXDUMP_HEXSTUFF_PER_SHORT+1], *hsp;
527: char asciistuff[100], *asp;
528: u_int maxlength = HEXDUMP_SHORTS_PER_LINE;
529:
530: /* sanity check */
531: if (!cp || length < 1)
532: return;
533:
534: nshorts = length / sizeof(u_short);
535: i = 0;
536: hsp = hexstuff;
537: asp = asciistuff;
538: while (--nshorts >= 0) {
539: s1 = *cp++;
540: s2 = *cp++;
541: (void)snprintf(hsp, sizeof(hexstuff) - (hsp - hexstuff),
542: " %02x%02x", s1, s2);
543: hsp += HEXDUMP_HEXSTUFF_PER_SHORT;
544: *(asp++) = (isgraph(s1) ? s1 : '.');
545: *(asp++) = (isgraph(s2) ? s2 : '.');
546: if (++i >= maxlength) {
547: i = 0;
548: *hsp = *asp = '\0';
549:
550: printw("\n0x%04X ", oset);
551: addch(VLINE);
552: printw("%-*s ", HEXDUMP_HEXSTUFF_PER_LINE, hexstuff);
553: addch(VLINE);
554: addch(' ');
555: addstr(asciistuff);
556:
557: hsp = hexstuff;
558: asp = asciistuff;
559: oset += HEXDUMP_BYTES_PER_LINE;
560:
561: redraw_lines++;
562: }
563: }
564: if (length & 1) {
565: s1 = *cp++;
566: (void)snprintf(hsp, sizeof(hexstuff) - (hsp - hexstuff),
567: " %02x", s1);
568: hsp += 3;
569: *(asp++) = (isgraph(s1) ? s1 : '.');
570: ++i;
571: }
572: if (i > 0) {
573: *hsp = *asp = '\0';
574:
575: printw("\n0x%04X ", oset);
576: addch(VLINE);
577: printw("%-*s ", HEXDUMP_HEXSTUFF_PER_LINE, hexstuff);
578: addch(VLINE);
579: addch(' ');
580: addstr(asciistuff);
581:
582: redraw_lines++;
583: }
584: }
585:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>