1
/*
2
 * Copyright (c) 2006  dustin sallings
3
 */
4
5
#include <stdio.h>
6
#include <stdlib.h>
7
#include <signal.h>
8
#include <unistd.h>
9
#include <time.h>
10
#include <sys/time.h>
11
#include <netdb.h>
12
#include <sys/types.h>
13
#include <sys/socket.h>
14
#include <netinet/in_systm.h>
15
#include <netinet/in.h>
16
#include <netinet/ip.h>
17
#include <netinet/tcp.h>
18
#include <netinet/udp.h>
19
#include <arpa/inet.h>
20
#include <pcap.h>
21
#include <string.h>
22
#include <assert.h>
23
24
#include "mymalloc.h"
25
#include "multisniff.h"
26
#include "hash.h"
27
28
static pcap_t  *pcap_socket = NULL;
29
static int      dlt_len = 0;
30
static struct hashtable *hash=NULL;
31
32
static int shouldExpunge=0;
33
static int shouldCleanup=0;
34
static int shuttingDown=0;
35
36
static void     filter_packet(u_char *, struct pcap_pkthdr *, u_char *);
37
static void     cleanup(int shouldFlush, int maxAge);
38
static void     signalShutdown(int);
39
static void     signalCleanup(int);
40
static void     signalExpunge(int);
41
42
static pcap_dumper_t *misc_packets=NULL;
43
44
static void exitCleanup() {
45
	hash_destroy(hash);
46
	if(misc_packets) {
47
		pcap_dump_close(misc_packets);
48
	}
49
	pcap_close(pcap_socket);
50
#ifdef MYMALLOC
51
	_mdebug_dump();
52
#endif /* MYMALLOC */
53
    exit(0);
54
}
55
56
static void
57
setupSignals(int refreshTime)
58
{
59
    sigset_t sigBlockSet;
60
    struct sigaction saShutdown, saCleanup, saExpunge;
61
    struct itimerval ival;
62
63
    sigemptyset(&sigBlockSet);
64
    sigaddset(&sigBlockSet, SIGHUP);
65
    if(sigprocmask(SIG_BLOCK, &sigBlockSet, NULL) < 0) {
66
        perror("sigprocmask");
67
        exit(1);
68
    }
69
70
    saShutdown.sa_handler=signalShutdown;
71
    saShutdown.sa_flags=0;
72
    sigemptyset(&saShutdown.sa_mask);
73
74
    if(sigaction(SIGINT, &saShutdown, NULL) < 0) {
75
        perror("sigaction(INT)");
76
        exit(1);
77
    }
78
    if(sigaction(SIGTERM, &saShutdown, NULL) < 0) {
79
        perror("sigaction(TERM)");
80
        exit(1);
81
    }
82
83
    saCleanup.sa_handler=signalCleanup;
84
    saCleanup.sa_flags=0;
85
    sigemptyset(&saCleanup.sa_mask);
86
87
    if(sigaction(SIGALRM,&saCleanup, NULL) < 0) {
88
        perror("sigaction(QUIT)");
89
        exit(1);
90
    }
91
92
    saExpunge.sa_handler=signalExpunge;
93
    saExpunge.sa_flags=0;
94
    sigemptyset(&saExpunge.sa_mask);
95
96
    if(sigaction(SIGQUIT, &saExpunge, NULL) < 0) {
97
        perror("sigaction(QUIT)");
98
        exit(1);
99
    }
100
101
    ival.it_interval.tv_usec=0;
102
    ival.it_value.tv_usec=0;
103
    ival.it_interval.tv_sec=refreshTime;
104
    ival.it_value.tv_sec=refreshTime;
105
    if(setitimer(ITIMER_REAL, &ival, NULL) < 0) {
106
        perror("setitimer");
107
        exit(1);
108
    }
109
}
110
111
static void
112
openMisc()
113
{
114
    char misc_filename[FILENAME_MAXLEN];
115
    time_t now=0;
116
117
    now=time(NULL);
118
    if(strftime(misc_filename, sizeof(misc_filename),
119
                "%Y%m%d-%H%M%S_misc.pcap", localtime(&now)) >= sizeof(misc_filename)) {
120
        fprintf(stderr,
121
                "Warning: not enough space for full filename, using %s\n",
122
                misc_filename);
123
    }
124
125
    assert(misc_packets == NULL);
126
    misc_packets=pcap_dump_open(pcap_socket, misc_filename);
127
128
    if(misc_packets == NULL) {
129
        fprintf(stderr, "Error opening dump file %s: %s\n",
130
                misc_filename, pcap_geterr(pcap_socket));
131
        exit(1);
132
    } else {
133
        printf("+ Created %s\n", misc_filename);
134
    }
135
}
136
137
static void
138
expunge()
139
{
140
    printf("# Cleaning up open pcap files\n");
141
    hash_destroy(hash);
142
    hash=hash_init(HASH_SIZE);
143
    if(misc_packets != NULL) {
144
        printf("# Closing misc_packets\n");
145
        pcap_dump_close(misc_packets);
146
        misc_packets=NULL;
147
    }
148
    shouldExpunge=0;
149
}
150
151
void
152
process(int flags, const char *intf, struct cleanupConfig conf,
153
        const char *outdir, char *filter)
154
{
155
    char            errbuf[PCAP_ERRBUF_SIZE];
156
    struct bpf_program prog;
157
    bpf_u_int32     netmask=0;
158
    int             flagdef;
159
160
    setupSignals(conf.refreshTime);
161
162
    if (flags & FLAG_BIT(FLAG_PROMISC))    {
163
        flagdef = 1;
164
    } else {
165
        flagdef = 0;
166
    }
167
168
    if (intf[0] == '/') {
169
        pcap_socket = pcap_open_offline(intf, errbuf);
170
    } else {
171
        pcap_socket = pcap_open_live(intf, 65535, flagdef, 1000, errbuf);
172
    }
173
174
    if (pcap_socket == NULL) {
175
        fprintf(stderr, "pcap_open_live: %s\n", errbuf);
176
        exit(-1);
177
    }
178
    switch (pcap_datalink(pcap_socket)) {
179
    case DLT_EN10MB:
180
        dlt_len = 14;
181
        break;
182
    case DLT_SLIP:
183
        dlt_len = 16;
184
        break;
185
    case DLT_PPP:
186
        dlt_len = 4;
187
        break;
188
    case DLT_FDDI:
189
        fprintf(stderr, "Sorry, can't do FDDI\n");
190
        exit(1);
191
        break;
192
    default:
193
        dlt_len = 4;
194
    }
195
196
    if (pcap_compile(pcap_socket, &prog, filter, 1, netmask) < 0) {
197
        fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(pcap_socket));
198
        exit(1);
199
    }
200
    if (pcap_setfilter(pcap_socket, &prog) < 0) {
201
        fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(pcap_socket));
202
        exit(1);
203
    }
204
205
    hash=hash_init(HASH_SIZE);
206
207
    fprintf(stderr,
208
            "interface: %s, filter: ``%s'', %spromiscuous\n",
209
            intf, filter, (flags & FLAG_BIT(FLAG_PROMISC)) ? "" : "NOT ");
210
    fflush(stderr);
211
212
    if(chdir(outdir) < 0) {
213
        perror("chdir");
214
        exit(1);
215
    }
216
217
    while (!shuttingDown) {
218
        int r = pcap_dispatch(pcap_socket, -1, (pcap_handler)filter_packet, NULL);
219
        if (r < 1) {
220
            shuttingDown = 1;
221
        }
222
        if(shouldExpunge) {
223
            expunge();
224
        }
225
        if(shouldCleanup) {
226
            cleanup(flags & FLAG_BIT(FLAG_FLUSH), conf.maxAge);
227
        }
228
    }
229
230
    exitCleanup();
231
}
232
233
static void
234
cleanup(int shouldFlush, int maxAge)
235
{
236
    static unsigned int last_pcount=0, last_dropcount=0, relative_counts=0;
237
    struct pcap_stat stats;
238
    struct hash_container *p;
239
    int i=0, watched=0, cleaned=0, maxDepth=0, empty=0;
240
    struct timeval now;
241
242
    shouldCleanup = 0;
243
244
    if(gettimeofday(&now, NULL) < 0) {
245
        perror("gettimeofday");
246
        exit(1);
247
    }
248
249
    /* Look for anything old enough to get cleaned up */
250
    for(i=0; i<hash->hashsize; i++) {
251
        p=hash->buckets[i];
252
        if(p) {
253
            int ci=0, depth=0;
254
            int toClose[1024];
255
            int closeOffset=0;
256
257
            for(; p; p=p->next) {
258
                depth++;
259
                maxDepth=depth > maxDepth ? depth : maxDepth;
260
#ifdef HAVE_PCAP_DUMP_FLUSH
261
                if(shouldFlush) {
262
                    pcap_dump_flush(p->pcap_dumper);
263
                }
264
#endif
265
                watched++;
266
                if(p->last_addition.tv_sec + maxAge < now.tv_sec) {
267
                    toClose[closeOffset++]=p->key;
268
                    assert(closeOffset < sizeof(toClose));
269
                }
270
            }
271
272
            for(ci=0; ci<closeOffset; ci++) {
273
                p=hash_find(hash, toClose[ci]);
274
                assert(p != NULL);
275
                printf("- Closing %s (too old)\n", p->filename);
276
                p=NULL; /* Can't use this anymore */
277
                hash_delete(hash, toClose[ci]);
278
                cleaned++;
279
            }
280
        } else {
281
            empty++;
282
        }
283
    }
284
285
#ifdef HAVE_PCAP_DUMP_FLUSH
286
    if(shouldFlush && misc_packets != NULL) {
287
        pcap_dump_flush(misc_packets);
288
    }
289
#endif
290
291
    if (pcap_stats(pcap_socket, &stats) == 0) {
292
        int processed=stats.ps_recv-last_pcount;
293
        int dropped=stats.ps_drop-last_dropcount;
294
        if(relative_counts == 0 && (processed < 0 || dropped < 0)) {
295
            printf("! pcap bug, counts went negative.  Compensating\n");
296
            relative_counts=1;
297
        }
298
        if(relative_counts) {
299
            processed=stats.ps_recv;
300
            dropped=stats.ps_drop;
301
        }
302
        printf("# Processed %d pkts, dropped %d, watched %d, cleaned %d,"
303
               " max depth %d, empty %d\n",
304
               processed, dropped, watched, cleaned, maxDepth, empty);
305
        last_pcount=stats.ps_recv;
306
        last_dropcount=stats.ps_drop;
307
    } else {
308
        printf("# Error getting pcap statistics: %s.  watched=%d, cleaned=%d\n",
309
               pcap_geterr(pcap_socket), watched, cleaned);
310
    }
311
}
312
313
/* this is the function that's called when pcap reads a packet */
314
static void
315
filter_packet(u_char * u, struct pcap_pkthdr * p, u_char * packet)
316
{
317
#define IP_SIZE  20
318
#define TCP_SIZE 20
319
320
    struct ip      *ip;
321
    struct ether_header *eth;
322
323
    eth=(struct ether_header *)packet;
324
325
    if(ntohs(eth->ether_type) == ETHERTYPE_IP) {
326
        /* p->len should never be smaller than the smallest possible packet */
327
        if (p->len < (dlt_len + IP_SIZE + TCP_SIZE)) {
328
            fprintf(stderr, "! Skipping packet that's too small.\n");
329
            return;
330
        }
331
332
        /* cast an ip pointer */
333
        ip = (struct ip *) (packet + dlt_len);
334
335
        hash_add(hash, pcap_socket, ntohl(ip->ip_src.s_addr), p, packet);
336
        hash_add(hash, pcap_socket, ntohl(ip->ip_dst.s_addr), p, packet);
337
    } else {
338
        /*
339
          printf("! Non-IP packet received (ether type 0x%x)\n",
340
          ntohs(eth->ether_type));
341
        */
342
        if(misc_packets == NULL) {
343
            openMisc();
344
        }
345
        pcap_dump((u_char *)misc_packets, p, packet);
346
    }
347
}
348
349
char *
350
ntoa(int a)
351
{
352
    static char     ret[40];
353
    int written=0;
354
355
    written=snprintf(ret, sizeof(ret)-1, "%d.%d.%d.%d",
356
                     ((a & 0xff000000) >> 24), ((a & 0x00ff0000) >> 16),
357
                     ((a & 0x0000ff00) >> 8), (a & 0x000000ff));
358
359
    assert(written < sizeof(ret));
360
361
    return(ret);
362
}
363
364
/* shut down in a controlled way, close log file, close socket, and exit */
365
static void
366
signalShutdown(int s)
367
{
368
    shuttingDown=1;
369
}
370
371
static void
372
signalExpunge(int s)
373
{
374
    shouldExpunge=1;
375
}
376
377
static void
378
signalCleanup(int s)
379
{
380
    shouldCleanup=1;
381
}