2 * iwcap.c - A simply radiotap capture utility outputting pcap dumps
4 * Copyright 2012 Jo-Philipp Wich <jow@openwrt.org>
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
32 #include <sys/ioctl.h>
33 #include <sys/socket.h>
34 #include <net/ethernet.h>
36 #include <netinet/in.h>
37 #include <linux/if_packet.h>
39 #define ARPHRD_IEEE80211_RADIOTAP 803
41 #define DLT_IEEE802_11_RADIO 127
42 #define LEN_IEEE802_11_HDR 32
44 #define FRAMETYPE_MASK 0xFC
45 #define FRAMETYPE_BEACON 0x80
46 #define FRAMETYPE_DATA 0x08
48 #if __BYTE_ORDER == __BIG_ENDIAN
49 #define le16(x) __bswap_16(x)
56 uint8_t run_daemon
= 0;
58 uint32_t frames_captured
= 0;
59 uint32_t frames_filtered
= 0;
61 int capture_sock
= -1;
62 const char *ifname
= NULL
;
66 uint32_t len
; /* number of slots */
67 uint32_t fill
; /* last used slot */
68 uint32_t slen
; /* slot size */
69 void *buf
; /* ring memory */
72 struct ringbuf_entry
{
73 uint32_t len
; /* used slot memory */
74 uint32_t olen
; /* original data size */
75 uint32_t sec
; /* epoch of slot creation */
76 uint32_t usec
; /* epoch microseconds */
79 typedef struct pcap_hdr_s
{
80 uint32_t magic_number
; /* magic number */
81 uint16_t version_major
; /* major version number */
82 uint16_t version_minor
; /* minor version number */
83 int32_t thiszone
; /* GMT to local correction */
84 uint32_t sigfigs
; /* accuracy of timestamps */
85 uint32_t snaplen
; /* max length of captured packets, in octets */
86 uint32_t network
; /* data link type */
89 typedef struct pcaprec_hdr_s
{
90 uint32_t ts_sec
; /* timestamp seconds */
91 uint32_t ts_usec
; /* timestamp microseconds */
92 uint32_t incl_len
; /* number of octets of packet saved in file */
93 uint32_t orig_len
; /* actual length of packet */
96 typedef struct ieee80211_radiotap_header
{
97 u_int8_t it_version
; /* set to 0 */
99 u_int16_t it_len
; /* entire length */
100 u_int32_t it_present
; /* fields present */
101 } __attribute__((__packed__
)) radiotap_hdr_t
;
108 strncpy(ifr
.ifr_name
, ifname
, IFNAMSIZ
);
110 if (ioctl(capture_sock
, SIOCGIFHWADDR
, &ifr
) < 0)
113 return (ifr
.ifr_hwaddr
.sa_family
== ARPHRD_IEEE80211_RADIOTAP
);
116 int set_promisc(int on
)
120 strncpy(ifr
.ifr_name
, ifname
, IFNAMSIZ
);
122 if (ioctl(capture_sock
, SIOCGIFFLAGS
, &ifr
) < 0)
125 if (on
&& !(ifr
.ifr_flags
& IFF_PROMISC
))
127 ifr
.ifr_flags
|= IFF_PROMISC
;
129 if (ioctl(capture_sock
, SIOCSIFFLAGS
, &ifr
))
134 else if (!on
&& (ifr
.ifr_flags
& IFF_PROMISC
))
136 ifr
.ifr_flags
&= ~IFF_PROMISC
;
138 if (ioctl(capture_sock
, SIOCSIFFLAGS
, &ifr
))
148 void sig_dump(int sig
)
153 void sig_teardown(int sig
)
159 void write_pcap_header(FILE *o
)
162 .magic_number
= 0xa1b2c3d4,
168 .network
= DLT_IEEE802_11_RADIO
171 fwrite(&ghdr
, 1, sizeof(ghdr
), o
);
174 void write_pcap_frame(FILE *o
, uint32_t *sec
, uint32_t *usec
,
175 uint16_t len
, uint16_t olen
)
182 gettimeofday(&tv
, NULL
);
190 fhdr
.ts_sec
= tv
.tv_sec
;
191 fhdr
.ts_usec
= tv
.tv_usec
;
193 fhdr
.orig_len
= olen
;
195 fwrite(&fhdr
, 1, sizeof(fhdr
), o
);
199 struct ringbuf
* ringbuf_init(uint32_t num_item
, uint16_t len_item
)
201 static struct ringbuf r
;
206 r
.buf
= malloc(num_item
* (len_item
+ sizeof(struct ringbuf_entry
)));
212 r
.slen
= (len_item
+ sizeof(struct ringbuf_entry
));
214 memset(r
.buf
, 0, num_item
* len_item
);
222 struct ringbuf_entry
* ringbuf_add(struct ringbuf
*r
)
225 struct ringbuf_entry
*e
;
227 gettimeofday(&t
, NULL
);
229 e
= r
->buf
+ (r
->fill
++ * r
->slen
);
232 memset(e
, 0, r
->slen
);
240 struct ringbuf_entry
* ringbuf_get(struct ringbuf
*r
, int i
)
242 struct ringbuf_entry
*e
= r
->buf
+ (((r
->fill
+ i
) % r
->len
) * r
->slen
);
250 void ringbuf_free(struct ringbuf
*r
)
253 memset(r
, 0, sizeof(*r
));
257 void msg(const char *fmt
, ...)
263 vsyslog(LOG_INFO
| LOG_USER
, fmt
, ap
);
265 vfprintf(stderr
, fmt
, ap
);
271 int main(int argc
, char **argv
)
274 struct ringbuf
*ring
;
275 struct ringbuf_entry
*e
;
276 struct sockaddr_ll local
= {
277 .sll_family
= AF_PACKET
,
278 .sll_protocol
= htons(ETH_P_ALL
)
281 radiotap_hdr_t
*rhdr
;
284 uint8_t pktbuf
[0xFFFF];
292 uint8_t streaming
= 0;
293 uint8_t foreground
= 0;
294 uint8_t filter_data
= 0;
295 uint8_t filter_beacon
= 0;
296 uint8_t header_written
= 0;
298 uint32_t ringsz
= 1024 * 1024; /* 1 Mbyte ring buffer */
299 uint16_t pktcap
= 256; /* truncate frames after 265KB */
301 const char *output
= NULL
;
304 while ((opt
= getopt(argc
, argv
, "i:r:c:o:sfhBD")) != -1)
310 if (!(local
.sll_ifindex
= if_nametoindex(ifname
)))
312 msg("Unknown interface '%s'\n", ifname
);
318 ringsz
= atoi(optarg
);
319 if (ringsz
< (3 * pktcap
))
321 msg("Ring size of %d bytes is too short, "
322 "must be at least %d bytes\n", ringsz
, 3 * pktcap
);
328 pktcap
= atoi(optarg
);
329 if (pktcap
<= (sizeof(radiotap_hdr_t
) + LEN_IEEE802_11_HDR
))
331 msg("Packet truncate after %d bytes is too short, "
332 "must be at least %d bytes\n",
333 pktcap
, sizeof(radiotap_hdr_t
) + LEN_IEEE802_11_HDR
);
361 " %s -i {iface} -s [-b] [-d]\n"
362 " %s -i {iface} -o {file} [-r len] [-c len] [-B] [-D] [-f]\n"
365 " Specify interface to use, must be in monitor mode and\n"
366 " produce IEEE 802.11 Radiotap headers.\n\n"
368 " Stream to stdout instead of Dumping to file on USR1.\n\n"
370 " Write current ringbuffer contents to given output file\n"
371 " on receipt of SIGUSR1.\n\n"
373 " Specify the amount of bytes to use for the ringbuffer.\n"
374 " The default length is %d bytes.\n\n"
376 " Truncate captured packets after given amount of bytes.\n"
377 " The default size limit is %d bytes.\n\n"
379 " Don't store beacon frames in ring, default is keep.\n\n"
381 " Don't store data frames in ring, default is keep.\n\n"
383 " Do not daemonize but keep running in foreground.\n\n"
385 " Display this help.\n\n",
386 argv
[0], argv
[0], ringsz
, pktcap
);
392 if (!streaming
&& !output
)
394 msg("No output file specified\n");
398 if (streaming
&& output
)
400 msg("The -s and -o options are exclusive\n");
404 if (streaming
&& isatty(1))
406 msg("Refusing to stream into a terminal\n");
410 if (!local
.sll_ifindex
)
412 msg("No interface specified\n");
418 msg("Bad interface: not ARPHRD_IEEE80211_RADIOTAP\n");
422 if ((capture_sock
= socket(PF_PACKET
, SOCK_RAW
, htons(ETH_P_ALL
))) < 0)
424 msg("Unable to create raw socket: %s\n",
429 if (bind(capture_sock
, (struct sockaddr
*)&local
, sizeof(local
)) == -1)
431 msg("Unable to bind to interface: %s\n",
443 msg("Unable to fork: %s\n", strerror(errno
));
449 freopen("/dev/null", "r", stdin
);
450 freopen("/dev/null", "w", stdout
);
451 freopen("/dev/null", "w", stderr
);
456 msg("Daemon launched ...\n");
461 msg("Monitoring interface %s ...\n", ifname
);
463 if (!(ring
= ringbuf_init(ringsz
/ pktcap
, pktcap
)))
465 msg("Unable to allocate ring buffer: %s\n",
470 msg(" * Using %d bytes ringbuffer with %d slots\n", ringsz
, ring
->len
);
471 msg(" * Truncating frames at %d bytes\n", pktcap
);
472 msg(" * Dumping data to file %s\n", output
);
474 signal(SIGUSR1
, sig_dump
);
478 msg("Monitoring interface %s ...\n", ifname
);
479 msg(" * Streaming data to stdout\n");
482 msg(" * Beacon frames are %sfiltered\n", filter_beacon
? "" : "not ");
483 msg(" * Data frames are %sfiltered\n", filter_data
? "" : "not ");
485 signal(SIGINT
, sig_teardown
);
486 signal(SIGTERM
, sig_teardown
);
488 promisc
= set_promisc(1);
495 msg("Shutting down ...\n");
507 msg("Dumping ring to %s ...\n", output
);
509 if (!(o
= fopen(output
, "w")))
511 msg("Unable to open %s: %s\n",
512 output
, strerror(errno
));
516 write_pcap_header(o
);
518 /* sig_dump packet buffer */
519 for (i
= 0, n
= 0; i
< ring
->len
; i
++)
521 if (!(e
= ringbuf_get(ring
, i
)))
524 write_pcap_frame(o
, &(e
->sec
), &(e
->usec
), e
->len
, e
->olen
);
525 fwrite((void *)e
+ sizeof(*e
), 1, e
->len
, o
);
531 msg(" * %d frames captured\n", frames_captured
);
532 msg(" * %d frames filtered\n", frames_filtered
);
533 msg(" * %d frames dumped\n", n
);
539 pktlen
= recvfrom(capture_sock
, pktbuf
, sizeof(pktbuf
), 0, NULL
, 0);
542 /* check received frametype, if we should filter it, rewind the ring */
543 rhdr
= (radiotap_hdr_t
*)pktbuf
;
545 if (pktlen
<= sizeof(radiotap_hdr_t
) || le16(rhdr
->it_len
) >= pktlen
)
551 frametype
= *(uint8_t *)(pktbuf
+ le16(rhdr
->it_len
));
553 if ((filter_data
&& (frametype
& FRAMETYPE_MASK
) == FRAMETYPE_DATA
) ||
554 (filter_beacon
&& (frametype
& FRAMETYPE_MASK
) == FRAMETYPE_BEACON
))
564 write_pcap_header(stdout
);
568 write_pcap_frame(stdout
, NULL
, NULL
, pktlen
, pktlen
);
569 fwrite(pktbuf
, 1, pktlen
, stdout
);
574 e
= ringbuf_add(ring
);
576 e
->len
= (pktlen
> pktcap
) ? pktcap
: pktlen
;
578 memcpy((void *)e
+ sizeof(*e
), pktbuf
, e
->len
);