Initial commit from project. lib dependencies need checking
[packeteer.git] / pcap.c
1 /*
2  * Packeteer 
3  * Copyright August 2007 TJ <linux@tjworld.net>
4  *
5  * Checks that network packets seen by libpcap are seen at the netfilters
6  * level and haven't been silently discarded by the kernel.
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program in the file LICENSE-GPLv3.txt;
20  * if not, you can view it online at http://www.gnu.org/copyleft/gpl.html
21  *
22  */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <sys/time.h>
29 #include <unistd.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <netinet/ether.h>
33 #include <netinet/ip.h>
34 #include <netinet/tcp.h>
35 #include <arpa/inet.h>
36 #include <netinet/if_ether.h>
37 #include <pcap.h>
38 #include <pthread.h>
39 #include "packeteer.h"
40
41 extern int size_ethernet; // sizes calculated once
42 extern int size_ip;
43 extern int size_tcp;
44 extern int verbose; // flag controlling debugging messages
45 extern unsigned int headers_done;
46 extern struct in_addr_filter *ip_addr_filters; // linked-list of source IP addresses to match
47 extern unsigned char tcp_flags_filter; // TCP flags to match (bitwise-OR)
48
49 unsigned long pcap_count = 0; // packet counter
50
51 int pcap_control = 0; // thread-running control signal
52 pthread_t pcap_thread_id;
53 pthread_mutex_t pcap_mutex = PTHREAD_MUTEX_INITIALIZER;
54
55 pcap_t *descr=NULL; // descriptor for pcap 
56
57 /* replacement for strcpy() that allocates the required memory internally */
58 char *my_strcpy(const char *src) {
59  int count = 0, length = 0;
60  char *dest=NULL;
61  
62  // find the length of the zero-terminated string
63  for (length=0; src[length] != 0; length++);
64
65  dest = (char *)malloc(length+1);
66  if (dest != NULL) {
67   for (count=0; count <= length; count++) {
68    dest[count] = src[count];
69   }
70  }
71  else
72   fprintf(stderr, "my_strcpy(): malloc() failed\n");
73  
74  return dest;
75 }
76
77 /* Called from pcap with pointer to a new packet */
78 void pcap_callback(u_char *useless, const struct pcap_pkthdr *pkthdr, const u_char *packet) {
79  int ip_addr_match = 0;
80  unsigned char tcp_flags_match = 0;
81  struct in_addr_filter *ip_walk = ip_addr_filters;
82  unsigned char *pos;
83  
84  struct pcap_pkthdr hdr;     /* pcap.h */
85  struct in_addr s;
86
87  const struct ether_header *ethernet; /* The ethernet header */
88  const struct iphdr *ip; /* The IP header */
89  const struct tcphdr *tcp; /* The TCP header */
90  const char *payload; /* Packet payload */
91
92  ethernet = (struct ether_header*)(packet);
93  ip = (struct iphdr*)(packet + size_ethernet);
94  tcp = (struct tcphdr*)(packet + size_ethernet + size_ip);
95  payload = (u_char *)(packet + size_ethernet + size_ip + size_tcp);
96
97  s.s_addr = ip->saddr;
98  
99  if (ip_addr_filters == NULL) 
100   ip_addr_match++; // no filter set, so report everything
101  else {
102   while(ip_walk != NULL) {
103    if (ip_walk->ip_addr.s_addr == s.s_addr) {
104     ip_addr_match++; // got a match
105     if (verbose) fprintf(stderr, "pcap: matched IP %s\n", inet_ntoa(ip_walk->ip_addr));
106    }
107    ip_walk = ip_walk->next;
108   }
109  }
110
111  if (ip_addr_match) { // IP address matches filter 
112  
113   // because tcphdr defines flags individually, to get the flags byte we have to index into the header
114   tcp_flags_match = *( ((unsigned char *)tcp) + 13)  & tcp_flags_filter;
115   if (verbose) fprintf(stderr, "pcap: result TCP flags 0x%2.2X, filter 0x%2.2X, TCP packet flags 0x%2.2X\n", tcp_flags_match, tcp_flags_filter,  *( ((unsigned char *)tcp) + 13) );
116  
117   if (tcp_flags_match) { // TCP flags match filter
118
119    if (!headers_done)
120     headers_print();
121  
122    fprintf(stderr, "pcap: %10s %10u %15s %4.4X %4.4X  %1d   %1d  %10u %10u %6d\n", 
123                    print_time(NULL), ++pcap_count, inet_ntoa(s), ntohs(ip->check), ntohs(tcp->check),
124                    tcp->syn, tcp->ack, tcp->seq, tcp->ack_seq, pkthdr->len);
125    fflush(stderr);
126
127    packet_pcap(pcap_count, ethernet, ip, tcp, pkthdr->len); // register the packet with the tracker
128   }
129  }
130 }
131
132 /* main loop runs in its own thread. Waits on data available for read on a pcap file descriptor.
133    When new data is available causes pcap to invoke the callback function.
134  */
135 void *pcap_thread(void *data) {
136  fd_set fd_wait;
137  struct timeval st;
138  int t, fd;
139
140  if (verbose) fprintf(stderr, "pcap_thread(): running\n");
141  fd = pcap_fileno(descr);
142  if (verbose) fprintf(stderr, "pcap: fd=%d\n", fd);
143
144  for(;;) {
145   FD_ZERO(&fd_wait);
146   FD_SET(fd, &fd_wait);
147
148   st.tv_sec  = 1;
149   st.tv_usec = 0;   /* 1000 = 1 second */
150
151   // wait up to 1 second for input
152   t=select(fd+1, &fd_wait, NULL, NULL, &st);
153
154   switch(t) {
155    case -1: // error
156     if (errno == EINTR || errno == EAGAIN) continue;
157     fprintf(stderr, "pcap: Error in main loop\n");
158     pcap_control = 0; // force loop to end
159     break;
160
161    case  0: // timed out, no trafffic
162     // fprintf(stderr, "p"); // for debugging: prove the timer is running
163     break;
164
165    default: // packets available
166     pcap_dispatch(descr, 1, (void *) pcap_callback, NULL);
167   }
168   if (pcap_control == 0) // parent thread's signal to quit
169    break;
170   
171  } // End of for(;;) 
172  
173  pthread_exit(NULL);
174 }
175
176 /*
177  * Prepares libpcap for capturing
178  * 
179  * @device select the device name: "eth1"
180  * @pcap_filter set a filter rule of the form: "dst host 10.2.3.4 and tcp port 80"
181  * @return 1 if successful; 0 if failed 
182  */
183 int pcap_init(char *device, char *pcap_filter) {
184  int i;
185  const u_char *packet;
186  bpf_u_int32 maskp;
187  bpf_u_int32 netp;
188  pcap_if_t *this_dev, *alldevsp;
189  int ret=1; // default return value, failed
190  char *dev=NULL; 
191  char *filter=NULL;
192  char errbuf[PCAP_ERRBUF_SIZE];
193  struct bpf_program fp;
194
195  if (verbose) fprintf(stderr, "calling pcap_findalldevs()\n");
196  if (pcap_findalldevs(&alldevsp, errbuf) == 0) {
197          if (verbose) fprintf(stderr, "pcap_findalldevs() done.\n");
198   this_dev = alldevsp;
199   while (this_dev != NULL) {
200           if (verbose) fprintf(stderr, "device %s\n",this_dev->name);
201    if (strcmp(device, this_dev->name) == 0) {
202         dev = this_dev->name;
203         break;
204    }
205    this_dev = this_dev->next;
206   } 
207  }
208  else {
209   fprintf(stderr, "Error: pcap_findalldevs()\n");        
210  }
211  if (dev) { // system has a device matching the one we want
212   if (verbose) fprintf(stderr, "pcap: initialising %s with rule \"%s\"\n", dev, pcap_filter);
213   pcap_lookupnet(dev, &netp, &maskp, errbuf);    
214   if (verbose) fprintf(stderr, "pcap_lookupnet() done\n");
215   /* open device for reading */
216   descr = pcap_open_live(dev, BUFSIZ, 0, 1024, errbuf);
217   if (verbose) fprintf(stderr, "pcap_open_live() done\n");
218
219   pcap_freealldevs(alldevsp); // free list memory
220   alldevsp=NULL;
221   if (verbose) fprintf(stderr, "pcap_freealldevs() done\n");
222
223   if(descr != NULL) {
224    // compile the 'filter' program
225           if (verbose) fprintf(stderr, "my_strcpy(\"%s\")\n", pcap_filter);
226    filter = my_strcpy(pcap_filter);
227    if (verbose) fprintf(stderr, "filter@%lx=%lx=\"%s\")\n",&filter, filter, filter);
228    if (verbose) fprintf(stderr, "pcap_compile(%lx, %lx, \"%s\", %d, %d)\n", descr, &fp, filter, 1, htonl(0xFFFF0000));
229    if(pcap_compile(descr, &fp, filter, 0, 0) != -1)     { // compile the rule
230            if (verbose) fprintf(stderr, "pcap_compile() done\n");
231     // set the compiled program as the filter
232     if(pcap_setfilter(descr, &fp) != -1) {
233      pcap_freecode(&fp); // release memory
234      free(filter); // release memory allocated by my_strcpy()
235      filter=NULL;
236      
237      // non-blocking mode
238      if (verbose) fprintf(stderr, "pcap_setnonblock()\n"); 
239      if(pcap_setnonblock(descr, 1, errbuf) != -1) {
240           ret = 0; // success
241      }
242      else {
243       fprintf(stderr, "Error pcap_setnonblock(): %s\n", dev, errbuf);
244       ret = 5;
245      }
246     }
247     else {
248          fprintf(stderr, "Error pcap_setfilter()\n");
249          ret = 4;
250     }  
251    }
252    else {
253     fprintf(stderr, "Error pcap_compile(): %s\n", pcap_geterr(descr));
254     ret = 3;
255    }
256   }
257   else { 
258    fprintf(stderr, "Error pcap_open_live(): %s\n",errbuf);
259    ret = 2;
260   }
261  }
262  else {
263   fprintf(stderr, "Error device %d not found\n", device);
264   ret = 1;
265  }
266  // clean-up if errors occurred
267  if (ret > 2) pcap_close(descr);
268  
269  if (verbose) fprintf (stderr, "leaving pcap_init()\n");
270  return ret;
271 }
272
273 /* Create and start main-loop thread */
274 int pcap_start(void) {
275  if (verbose) fprintf(stderr, "pcap_start(): starting\n");
276  pcap_control = 1; // run thread
277  if (verbose) fprintf(stderr, "pcap_start(): creating thread\n");
278  pthread_create(&pcap_thread_id, NULL, pcap_thread, NULL);      
279  if (verbose) fprintf(stderr, "pcap_start(): done\n");
280  return 0; // success
281 }
282
283 /* Stop main-loop thread and clean up */
284 void pcap_exit(void) {
285  if (verbose) fprintf(stderr, "pcap_exit(): exiting\n");
286  if (pcap_control != 0) {
287   if (verbose) fprintf(stderr, "pcap: stopping thread\n");
288   pthread_mutex_lock(&pcap_mutex);
289   pcap_control = 0; // signal thread to stop
290   pthread_mutex_unlock(&pcap_mutex);
291
292   // blocks until thread ends
293   pthread_join(pcap_thread_id, NULL);
294  }
295  pcap_close(descr);
296  if (verbose) fprintf(stderr, "pcap_exit(): done\n");
297  }