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