Initial commit from project. lib dependencies need checking
[packeteer.git] / netfilters.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 <unistd.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <netinet/ether.h>
31 #include <netinet/ip.h>
32 #include <netinet/tcp.h>
33 #include <arpa/inet.h>
34 #include <netinet/if_ether.h>
35 #include <linux/netfilter.h>
36 #include <libnetfilter_queue/libnetfilter_queue.h>
37 #include <pthread.h>
38 #include "packeteer.h"
39 #include "netfilters.h"
40
41 extern int size_ip; // sizes calculated once
42 extern int size_tcp;
43 extern int verbose; // flag controlling debugging messages
44 extern unsigned int headers_done;
45 extern struct in_addr_filter *ip_addr_filters; // linked-list of source IP addresses to match
46 extern unsigned char tcp_flags_filter; // TCP flags to match (bitwise-OR)
47
48 unsigned long netfilters_count = 0; // packet counter
49
50 int netfilters_control = 0; // thread-running control signal
51 pthread_t netfilters_thread_id;
52 pthread_mutex_t netfilters_mutex = PTHREAD_MUTEX_INITIALIZER;
53
54 unsigned long netfilters_lag; // simulate lag in circuit
55
56 // netfilters_queue handles
57 struct nfq_handle *handle = NULL;
58 struct nfq_q_handle *queue_handle = NULL;
59
60 // identifies the unique netfilters queue (set by iptables with target -j NFQUEUE --queue-num)
61 // NFQUEUE is defined in netfilters.h
62 u_int16_t queue_num = NFQUEUE; 
63
64
65 /* Called from netfilters with pointer to a new packet */
66 int netfilters_callback(struct nfq_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data) {
67  int ip_addr_match = 0;
68  unsigned char tcp_flags_match = 0;
69  struct in_addr_filter *ip_walk = ip_addr_filters;
70  
71  u_int32_t packet_len = 0;
72  char *packet = NULL; // raw packet (Ethernet header has been stripped off before this point)
73
74  const struct iphdr *ip = NULL; // The IP header
75  const struct tcphdr *tcp = NULL; // The TCP header
76  const char *payload = NULL; 
77
78  struct nfqnl_msg_packet_hdr *ph = nfq_get_msg_packet_hdr(nfa); // packet header - needed to get packet id to set the verdict
79  int id = 0; // unique packet ID assigned by netfilters
80  
81  struct in_addr s;
82
83  packet_len = nfq_get_payload(nfa, &packet); // set the pointer to the packet data and find out how large it is
84  
85  // set pointers to structures within the packet
86  ip = (struct iphdr*)(packet);
87  tcp = (struct tcphdr*)(packet + size_ip);
88  payload = (u_char *)(packet + size_ip + size_tcp);
89
90  s.s_addr = ip->saddr; // the source IP address
91
92  if (ip_addr_filters == NULL) 
93   ip_addr_match++; // no filter set, so report everything
94  else {
95   while(ip_walk != NULL) {
96    if (ip_walk->ip_addr.s_addr == s.s_addr) {
97     ip_addr_match++; // got a match
98     if (verbose) fprintf(stderr, "netf: matched IP %s\n", inet_ntoa(ip_walk->ip_addr));
99    }
100    ip_walk = ip_walk->next;
101   }
102  }
103
104  if (ip_addr_match) { // IP address matches filter
105          
106   // because tcphdr defines flags individually, to get the byte we have to index into the header
107   tcp_flags_match = *( ((unsigned char *)tcp) + 13)  & tcp_flags_filter;
108   if (verbose) fprintf(stderr, "netf: result TCP flags 0x%2.2X, filter 0x%2.2X, TCP packet flags 0x%2.2X\n",
109                                 tcp_flags_match, tcp_flags_filter,  *(((unsigned char *)tcp) + 13));
110  
111   if (tcp_flags_match) { // TCP flags match filter
112  
113    if (!headers_done)
114     headers_print();
115
116    fprintf(stderr, "netf: %10s %10u %15s %4.4X %4.4X  %1d   %1d  %10u %10u\n",
117                    print_time(NULL), ++netfilters_count, inet_ntoa(s), ntohs(ip->check), ntohs(tcp->check),
118                    tcp->syn, tcp->ack, tcp->seq, tcp->ack_seq);
119    fflush(stderr);
120    
121    if (netfilters_lag > 0) usleep(netfilters_lag); // simulate lag in circuit
122    
123    packet_netf(netfilters_count, ip, tcp); // register the packet with the tracker
124   }
125  }
126  
127  // tell netfilters to accept the packet
128  if (ph) id = ntohl(ph->packet_id); // got to determine the netfilters packet id to be able to set the verdict
129  nfq_set_verdict(queue_handle, id, NF_ACCEPT, packet_len, packet);
130  
131  return 0; // success   
132 }
133
134 /* main loop runs in its own thread. Waits on data available for read on a netfilters file descriptor.
135    When new data is available causes netfilters to invoke the callback function.
136  */
137 void *netfilters_thread(void *data) {
138  fd_set fd_wait;
139  struct timeval st;
140  int t, rv, fd;
141  unsigned char buffer[BUFSIZ];
142  
143  if (verbose) fprintf(stderr, "netfilters_thread(): running\n");
144  fd = nfq_fd(handle); // get the queue file descriptor
145  if (verbose) fprintf(stderr, "netf: fd=%d\n", fd);
146  
147  for(;;) {  // loop until something causes a break
148   FD_ZERO(&fd_wait); // prepare the fd's to wait on
149   FD_SET(fd, &fd_wait);
150
151   st.tv_sec  = 1;
152   st.tv_usec = 0;   /* 1000 = 1 second */
153
154   // wait up to 1 second for input
155   t = select(fd+1, &fd_wait, NULL, NULL, &st);
156
157   switch(t) {
158    case -1: // error
159     if (errno == EINTR || errno == EAGAIN) continue; // these aren't 'bad'
160
161     fprintf(stderr, "netf: Error in main loop\n");
162
163     pthread_mutex_lock(&netfilters_mutex);
164     netfilters_control = 0; // force the loop to end
165     pthread_mutex_unlock(&netfilters_mutex);
166     break;
167     
168    case  0: // timed out, no trafffic
169     // fprintf(stderr, "n"); // for debugging: prove the timer is running
170     break;
171
172    default: // packets available
173         // empty the file descriptor stream
174         rv = recv(fd, (void *) buffer, sizeof(buffer), 0);
175     nfq_handle_packet(handle, (char *) buffer, rv); // initiate the call to netfilters_callback()
176         
177   }
178   if (netfilters_control == 0) // parent thread's signal to quit
179    break;
180   
181  } // End of for(;;) 
182  
183  pthread_exit(NULL);
184 }
185
186 /* Configure a netfilters_queue for monitoring */
187 int netfilters_init(u_int16_t queue) {
188  int ret = 1; // default return status: failed
189  
190  if (verbose) fprintf(stderr, "netfilters_init(): initialising\n");
191  handle = nfq_open(); // open a netfilters queue
192  if (handle != NULL) {
193   if (nfq_unbind_pf(handle, PF_INET) == 0) {
194    if (nfq_bind_pf(handle, PF_INET) == 0) { // bind to IPv4 packet family
195            
196         nfq_unbind_pf(handle, PF_INET6); // bind for IPv6
197         nfq_bind_pf(handle, PF_INET6); // doesn't matter if either of these fail since we're not using IPv6
198         
199         // get a handle to the queue that matches the iptables rule that is to feed packets in
200     queue_handle = nfq_create_queue(handle, queue, (nfq_callback *) &netfilters_callback, NULL);
201         if (queue_handle != NULL) {
202          queue_num = queue; // confirm queue being used
203          if (verbose) fprintf(stderr, "netf: -j NFQUEUE --queue-num %d\n", queue_num);   
204          if (nfq_set_mode(queue_handle, NFQNL_COPY_PACKET, 0xffff) == 0) {
205       ret = 0; // success
206          }
207          else {
208           fprintf(stderr, "netf: Error setting queue mode COPY_PACKET\n");
209           ret = 5;
210          }
211         }
212         else {
213          fprintf(stderr, "netf: Error creating queue\n");
214          ret = 4;
215         }
216    }
217    else {
218     fprintf(stderr, "netf: Error binding packet family\n");
219     ret = 3;
220    }
221   }
222   else {
223    fprintf(stderr, "netf: Error unbinding packet family\n");
224    ret = 2;
225   }
226  }
227  else {
228   fprintf(stderr, "netf: Error opening queue\n");
229   ret = 1;
230  }
231  // clean-up if any errors occurred
232  if (ret > 4) nfq_destroy_queue(queue_handle);
233  if (ret > 1) nfq_close(handle);
234  
235  if (verbose) fprintf(stderr, "netfilters_init(): done\n");
236  return ret;
237 }
238
239 /* Create and start the main-loop thread */
240 int netfilters_start(void) {
241  if (verbose) fprintf(stderr, "netfilters_start(): starting\n");
242  netfilters_control = 1; // run thread
243  if (verbose) fprintf(stderr, "netfilters_start(): creating thread\n");
244  pthread_create(&netfilters_thread_id, NULL, netfilters_thread, NULL);  
245  if (verbose) fprintf(stderr, "netfilters_start(): done\n");
246  return 0;
247 }
248
249 /* Stop the main-loop thread and clean up */
250 void netfilters_exit(void) {
251  if (verbose) fprintf(stderr, "netfilters_exit(): exiting\n");
252  if (netfilters_control != 0) {
253   if (verbose) fprintf(stderr, "netf: stopping thread\n");
254   pthread_mutex_lock(&netfilters_mutex);
255   netfilters_control = 0; // signal thread to stop
256   pthread_mutex_unlock(&netfilters_mutex);
257   // blocks until thread ends
258   pthread_join(netfilters_thread_id, NULL);
259  }
260  nfq_close(handle);
261  
262  if (verbose) fprintf(stderr, "netfilters_exit(): done\n");
263 }