Root/package/network/utils/iwcap/src/iwcap.c

1/*
2 * iwcap.c - A simply radiotap capture utility outputting pcap dumps
3 *
4 * Copyright 2012 Jo-Philipp Wich <jow@openwrt.org>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19
20#include <stdio.h>
21#include <stdint.h>
22#include <stdlib.h>
23#include <stdarg.h>
24#include <unistd.h>
25#include <string.h>
26#include <signal.h>
27#include <syslog.h>
28#include <errno.h>
29#include <sys/stat.h>
30#include <sys/time.h>
31#include <sys/ioctl.h>
32#include <sys/socket.h>
33#include <net/ethernet.h>
34#include <net/if.h>
35#include <netinet/in.h>
36#include <linux/if_packet.h>
37
38#define ARPHRD_IEEE80211_RADIOTAP 803
39
40#define DLT_IEEE802_11_RADIO 127
41#define LEN_IEEE802_11_HDR 32
42
43#define FRAMETYPE_MASK 0xFC
44#define FRAMETYPE_BEACON 0x80
45#define FRAMETYPE_DATA 0x08
46
47#if __BYTE_ORDER == __BIG_ENDIAN
48#define le16(x) __bswap_16(x)
49#else
50#define le16(x) (x)
51#endif
52
53uint8_t run_dump = 0;
54uint8_t run_stop = 0;
55uint8_t run_daemon = 0;
56
57uint32_t frames_captured = 0;
58uint32_t frames_filtered = 0;
59
60int capture_sock = -1;
61const char *ifname = NULL;
62
63
64struct ringbuf {
65    uint32_t len; /* number of slots */
66    uint32_t fill; /* last used slot */
67    uint32_t slen; /* slot size */
68    void *buf; /* ring memory */
69};
70
71struct ringbuf_entry {
72    uint32_t len; /* used slot memory */
73    uint32_t olen; /* original data size */
74    uint32_t sec; /* epoch of slot creation */
75    uint32_t usec; /* epoch microseconds */
76};
77
78typedef struct pcap_hdr_s {
79    uint32_t magic_number; /* magic number */
80    uint16_t version_major; /* major version number */
81    uint16_t version_minor; /* minor version number */
82    int32_t thiszone; /* GMT to local correction */
83    uint32_t sigfigs; /* accuracy of timestamps */
84    uint32_t snaplen; /* max length of captured packets, in octets */
85    uint32_t network; /* data link type */
86} pcap_hdr_t;
87
88typedef struct pcaprec_hdr_s {
89    uint32_t ts_sec; /* timestamp seconds */
90    uint32_t ts_usec; /* timestamp microseconds */
91    uint32_t incl_len; /* number of octets of packet saved in file */
92    uint32_t orig_len; /* actual length of packet */
93} pcaprec_hdr_t;
94
95typedef struct ieee80211_radiotap_header {
96    u_int8_t it_version; /* set to 0 */
97    u_int8_t it_pad;
98    u_int16_t it_len; /* entire length */
99    u_int32_t it_present; /* fields present */
100} __attribute__((__packed__)) radiotap_hdr_t;
101
102
103int check_type(void)
104{
105    struct ifreq ifr;
106
107    strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
108
109    if (ioctl(capture_sock, SIOCGIFHWADDR, &ifr) < 0)
110        return -1;
111
112    return (ifr.ifr_hwaddr.sa_family == ARPHRD_IEEE80211_RADIOTAP);
113}
114
115int set_promisc(int on)
116{
117    struct ifreq ifr;
118
119    strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
120
121    if (ioctl(capture_sock, SIOCGIFFLAGS, &ifr) < 0)
122        return -1;
123
124    if (on && !(ifr.ifr_flags & IFF_PROMISC))
125    {
126        ifr.ifr_flags |= IFF_PROMISC;
127
128        if (ioctl(capture_sock, SIOCSIFFLAGS, &ifr))
129            return -1;
130
131        return 1;
132    }
133    else if (!on && (ifr.ifr_flags & IFF_PROMISC))
134    {
135        ifr.ifr_flags &= ~IFF_PROMISC;
136
137        if (ioctl(capture_sock, SIOCSIFFLAGS, &ifr))
138            return -1;
139
140        return 1;
141    }
142
143    return 0;
144}
145
146
147void sig_dump(int sig)
148{
149    run_dump = 1;
150}
151
152void sig_teardown(int sig)
153{
154    run_stop = 1;
155}
156
157
158void write_pcap_header(FILE *o)
159{
160    pcap_hdr_t ghdr = {
161        .magic_number = 0xa1b2c3d4,
162        .version_major = 2,
163        .version_minor = 4,
164        .thiszone = 0,
165        .sigfigs = 0,
166        .snaplen = 0xFFFF,
167        .network = DLT_IEEE802_11_RADIO
168    };
169
170    fwrite(&ghdr, 1, sizeof(ghdr), o);
171}
172
173void write_pcap_frame(FILE *o, uint32_t *sec, uint32_t *usec,
174                      uint16_t len, uint16_t olen)
175{
176    struct timeval tv;
177    pcaprec_hdr_t fhdr;
178
179    if (!sec || !usec)
180    {
181        gettimeofday(&tv, NULL);
182    }
183    else
184    {
185        tv.tv_sec = *sec;
186        tv.tv_usec = *usec;
187    }
188
189    fhdr.ts_sec = tv.tv_sec;
190    fhdr.ts_usec = tv.tv_usec;
191    fhdr.incl_len = len;
192    fhdr.orig_len = olen;
193
194    fwrite(&fhdr, 1, sizeof(fhdr), o);
195}
196
197
198struct ringbuf * ringbuf_init(uint32_t num_item, uint16_t len_item)
199{
200    static struct ringbuf r;
201
202    if (len_item <= 0)
203        return NULL;
204
205    r.buf = malloc(num_item * (len_item + sizeof(struct ringbuf_entry)));
206
207    if (r.buf)
208    {
209        r.len = num_item;
210        r.fill = 0;
211        r.slen = (len_item + sizeof(struct ringbuf_entry));
212
213        memset(r.buf, 0, num_item * len_item);
214
215        return &r;
216    }
217
218    return NULL;
219}
220
221struct ringbuf_entry * ringbuf_add(struct ringbuf *r)
222{
223    struct timeval t;
224    struct ringbuf_entry *e;
225
226    gettimeofday(&t, NULL);
227
228    e = r->buf + (r->fill++ * r->slen);
229    r->fill %= r->len;
230
231    memset(e, 0, r->slen);
232
233    e->sec = t.tv_sec;
234    e->usec = t.tv_usec;
235
236    return e;
237}
238
239struct ringbuf_entry * ringbuf_get(struct ringbuf *r, int i)
240{
241    struct ringbuf_entry *e = r->buf + (((r->fill + i) % r->len) * r->slen);
242
243    if (e->len > 0)
244        return e;
245
246    return NULL;
247}
248
249void ringbuf_free(struct ringbuf *r)
250{
251    free(r->buf);
252    memset(r, 0, sizeof(*r));
253}
254
255
256void msg(const char *fmt, ...)
257{
258    va_list ap;
259    va_start(ap, fmt);
260
261    if (run_daemon)
262        vsyslog(LOG_INFO | LOG_USER, fmt, ap);
263    else
264        vfprintf(stderr, fmt, ap);
265
266    va_end(ap);
267}
268
269
270int main(int argc, char **argv)
271{
272    int i, n;
273    struct ringbuf *ring;
274    struct ringbuf_entry *e;
275    struct sockaddr_ll local = {
276        .sll_family = AF_PACKET,
277        .sll_protocol = htons(ETH_P_ALL)
278    };
279
280    radiotap_hdr_t *rhdr;
281
282    uint8_t frametype;
283    uint8_t pktbuf[0xFFFF];
284    ssize_t pktlen;
285
286    FILE *o;
287
288    int opt;
289
290    uint8_t promisc = 0;
291    uint8_t streaming = 0;
292    uint8_t foreground = 0;
293    uint8_t filter_data = 0;
294    uint8_t filter_beacon = 0;
295    uint8_t header_written = 0;
296
297    uint32_t ringsz = 1024 * 1024; /* 1 Mbyte ring buffer */
298    uint16_t pktcap = 256; /* truncate frames after 265KB */
299
300    const char *output = NULL;
301
302
303    while ((opt = getopt(argc, argv, "i:r:c:o:sfhBD")) != -1)
304    {
305        switch (opt)
306        {
307        case 'i':
308            ifname = optarg;
309            if (!(local.sll_ifindex = if_nametoindex(ifname)))
310            {
311                msg("Unknown interface '%s'\n", ifname);
312                return 2;
313            }
314            break;
315
316        case 'r':
317            ringsz = atoi(optarg);
318            if (ringsz < (3 * pktcap))
319            {
320                msg("Ring size of %d bytes is too short, "
321                    "must be at least %d bytes\n", ringsz, 3 * pktcap);
322                return 3;
323            }
324            break;
325
326        case 'c':
327            pktcap = atoi(optarg);
328            if (pktcap <= (sizeof(radiotap_hdr_t) + LEN_IEEE802_11_HDR))
329            {
330                msg("Packet truncate after %d bytes is too short, "
331                    "must be at least %d bytes\n",
332                    pktcap, sizeof(radiotap_hdr_t) + LEN_IEEE802_11_HDR);
333                return 4;
334            }
335            break;
336
337        case 's':
338            streaming = 1;
339            break;
340
341        case 'o':
342            output = optarg;
343            break;
344
345        case 'B':
346            filter_beacon = 1;
347            break;
348
349        case 'D':
350            filter_data = 1;
351            break;
352
353        case 'f':
354            foreground = 1;
355            break;
356
357        case 'h':
358            msg(
359                "Usage:\n"
360                " %s -i {iface} -s [-b] [-d]\n"
361                " %s -i {iface} -o {file} [-r len] [-c len] [-B] [-D] [-f]\n"
362                "\n"
363                " -i iface\n"
364                " Specify interface to use, must be in monitor mode and\n"
365                " produce IEEE 802.11 Radiotap headers.\n\n"
366                " -s\n"
367                " Stream to stdout instead of Dumping to file on USR1.\n\n"
368                " -o file\n"
369                " Write current ringbuffer contents to given output file\n"
370                " on receipt of SIGUSR1.\n\n"
371                " -r len\n"
372                " Specify the amount of bytes to use for the ringbuffer.\n"
373                " The default length is %d bytes.\n\n"
374                " -c len\n"
375                " Truncate captured packets after given amount of bytes.\n"
376                " The default size limit is %d bytes.\n\n"
377                " -B\n"
378                " Don't store beacon frames in ring, default is keep.\n\n"
379                " -D\n"
380                " Don't store data frames in ring, default is keep.\n\n"
381                " -f\n"
382                " Do not daemonize but keep running in foreground.\n\n"
383                " -h\n"
384                " Display this help.\n\n",
385                argv[0], argv[0], ringsz, pktcap);
386
387            return 1;
388        }
389    }
390
391    if (!streaming && !output)
392    {
393        msg("No output file specified\n");
394        return 1;
395    }
396
397    if (streaming && output)
398    {
399        msg("The -s and -o options are exclusive\n");
400        return 1;
401    }
402
403    if (streaming && isatty(1))
404    {
405        msg("Refusing to stream into a terminal\n");
406        return 1;
407    }
408
409    if (!local.sll_ifindex)
410    {
411        msg("No interface specified\n");
412        return 2;
413    }
414
415    if (!check_type())
416    {
417        msg("Bad interface: not ARPHRD_IEEE80211_RADIOTAP\n");
418        return 2;
419    }
420
421    if ((capture_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0)
422    {
423        msg("Unable to create raw socket: %s\n",
424                strerror(errno));
425        return 6;
426    }
427
428    if (bind(capture_sock, (struct sockaddr *)&local, sizeof(local)) == -1)
429    {
430        msg("Unable to bind to interface: %s\n",
431            strerror(errno));
432        return 7;
433    }
434
435    if (!streaming)
436    {
437        if (!foreground)
438        {
439            switch (fork())
440            {
441                case -1:
442                    msg("Unable to fork: %s\n", strerror(errno));
443                    return 8;
444
445                case 0:
446                    umask(0077);
447                    chdir("/");
448                    freopen("/dev/null", "r", stdin);
449                    freopen("/dev/null", "w", stdout);
450                    freopen("/dev/null", "w", stderr);
451                    run_daemon = 1;
452                    break;
453
454                default:
455                    msg("Daemon launched ...\n");
456                    return 0;
457            }
458        }
459
460        msg("Monitoring interface %s ...\n", ifname);
461
462        if (!(ring = ringbuf_init(ringsz / pktcap, pktcap)))
463        {
464            msg("Unable to allocate ring buffer: %s\n",
465                strerror(errno));
466            return 5;
467        }
468
469        msg(" * Using %d bytes ringbuffer with %d slots\n", ringsz, ring->len);
470        msg(" * Truncating frames at %d bytes\n", pktcap);
471        msg(" * Dumping data to file %s\n", output);
472
473        signal(SIGUSR1, sig_dump);
474    }
475    else
476    {
477        msg("Monitoring interface %s ...\n", ifname);
478        msg(" * Streaming data to stdout\n");
479    }
480
481    msg(" * Beacon frames are %sfiltered\n", filter_beacon ? "" : "not ");
482    msg(" * Data frames are %sfiltered\n", filter_data ? "" : "not ");
483
484    signal(SIGINT, sig_teardown);
485    signal(SIGTERM, sig_teardown);
486
487    promisc = set_promisc(1);
488
489    /* capture loop */
490    while (1)
491    {
492        if (run_stop)
493        {
494            msg("Shutting down ...\n");
495
496            if (promisc)
497                set_promisc(0);
498
499            if (ring)
500                ringbuf_free(ring);
501
502            return 0;
503        }
504        else if (run_dump)
505        {
506            msg("Dumping ring to %s ...\n", output);
507
508            if (!(o = fopen(output, "w")))
509            {
510                msg("Unable to open %s: %s\n",
511                    output, strerror(errno));
512            }
513            else
514            {
515                write_pcap_header(o);
516
517                /* sig_dump packet buffer */
518                for (i = 0, n = 0; i < ring->len; i++)
519                {
520                    if (!(e = ringbuf_get(ring, i)))
521                        continue;
522
523                    write_pcap_frame(o, &(e->sec), &(e->usec), e->len, e->olen);
524                    fwrite((void *)e + sizeof(*e), 1, e->len, o);
525                    n++;
526                }
527
528                fclose(o);
529
530                msg(" * %d frames captured\n", frames_captured);
531                msg(" * %d frames filtered\n", frames_filtered);
532                msg(" * %d frames dumped\n", n);
533            }
534
535            run_dump = 0;
536        }
537
538        pktlen = recvfrom(capture_sock, pktbuf, sizeof(pktbuf), 0, NULL, 0);
539        frames_captured++;
540
541        /* check received frametype, if we should filter it, rewind the ring */
542        rhdr = (radiotap_hdr_t *)pktbuf;
543
544        if (pktlen <= sizeof(radiotap_hdr_t) || le16(rhdr->it_len) >= pktlen)
545        {
546            frames_filtered++;
547            continue;
548        }
549
550        frametype = *(uint8_t *)(pktbuf + le16(rhdr->it_len));
551
552        if ((filter_data && (frametype & FRAMETYPE_MASK) == FRAMETYPE_DATA) ||
553            (filter_beacon && (frametype & FRAMETYPE_MASK) == FRAMETYPE_BEACON))
554        {
555            frames_filtered++;
556            continue;
557        }
558
559        if (streaming)
560        {
561            if (!header_written)
562            {
563                write_pcap_header(stdout);
564                header_written = 1;
565            }
566
567            write_pcap_frame(stdout, NULL, NULL, pktlen, pktlen);
568            fwrite(pktbuf, 1, pktlen, stdout);
569            fflush(stdout);
570        }
571        else
572        {
573            e = ringbuf_add(ring);
574            e->olen = pktlen;
575            e->len = (pktlen > pktcap) ? pktcap : pktlen;
576
577            memcpy((void *)e + sizeof(*e), pktbuf, e->len);
578        }
579    }
580
581    return 0;
582}
583

Archive Download this file



interactive