Flowgrind
Advanced TCP traffic generator
fg_pcap.c
Go to the documentation of this file.
1 
6 /*
7  * Copyright (C) 2010-2013 Christian Samsel <christian.samsel@rwth-aachen.de>
8  * Copyright (C) 2009 Tim Kosse <tim.kosse@gmx.de>
9  * Copyright (C) 2007-2008 Daniel Schaffrath <daniel.schaffrath@mac.com>
10  *
11  * This file is part of Flowgrind.
12  *
13  * Flowgrind is free software: you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation, either version 3 of the License, or
16  * (at your option) any later version.
17  *
18  * Flowgrind is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with Flowgrind. If not, see <http://www.gnu.org/licenses/>.
25  *
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif /* HAVE_CONFIG_H */
31 
32 #include <sys/socket.h>
33 #include <sys/types.h>
34 #include <netinet/in.h>
35 #include <netinet/in_systm.h>
36 #include <netinet/ip.h>
37 #include <arpa/inet.h>
38 #include <netinet/if_ether.h>
39 #include <stdbool.h>
40 #include <string.h>
41 #include <syslog.h>
42 #include <stdlib.h>
43 #include <time.h>
44 #include <unistd.h>
45 #include <pthread.h>
46 #include <errno.h>
47 #include <pcap.h>
48 #include <netdb.h>
49 
50 #include "debug.h"
51 #include "fg_socket.h"
52 #include "fg_time.h"
53 #include "fg_string.h"
54 #include "fg_log.h"
55 #include "daemon.h"
56 #include "fg_pcap.h"
57 
58 /* OS X hasn't defined pthread_barrier */
59 #ifndef HAVE_PTHREAD_BARRIER
60 #include "fg_barrier.h"
61 #endif
62 
63 /* PCAP snapshot length */
64 #define PCAP_SNAPLEN 130
65 
66 /* PCAP filter string */
67 #define PCAP_FILTER "tcp"
68 
69 /* Flag whether to use promiscuous mode */
70 #define PCAP_PROMISC 0
71 
72 /* Error message buffer filled by the pcap library in case of an error */
73 static char errbuf[PCAP_ERRBUF_SIZE] = "";
74 
75 /* Barrier to block fg_pcap_go() until worker thread reaches its main loop */
77 
78 /* Pointer to the first element in a list containing all 'pcapable' devices */
79 static pcap_if_t *alldevs;
80 
81 /* Flag if traffic is currently dumped */
82 static bool dumping;
83 
84 int fg_pcap_init(void)
85 {
86  /* initalize *alldevs for later use */
87  if (pcap_findalldevs(&alldevs, errbuf) == -1) {
88  logging(LOG_WARNING,"error in pcap_findalldevs: %s\n", errbuf);
89  return -1;
90  }
91 
92 #ifdef DEBUG
93  for (pcap_if_t *d = alldevs; d; d = d->next) {
94  char *devdes = NULL;
95  if (asprintf(&devdes, "%s: ", d->name) == -1)
96  return -1;
97 
98  for (pcap_addr_t *a = d->addresses; a; a = a->next) {
99  if (!a->addr)
100  continue;
101 
102  asprintf_append(&devdes, "a=%s",
103  fg_nameinfo(a->addr,
104  sizeof(struct sockaddr)));
105  if (a->next)
106  asprintf_append(&devdes, ", ");
107  }
108  DEBUG_MSG(LOG_ERR, "pcap: found pcapable device (%s)", devdes);
109  free(devdes);
110  }
111 #endif /* DEBUG*/
112 
113  pthread_barrier_init(&pcap_barrier, NULL, 2);
114  return 0;
115 }
116 
125 void fg_pcap_cleanup(void *arg)
126 {
127  /* signature of pthread_create() requires that all arguments must be
128  * passed by reference and cast to (void *) */
129  struct flow *flow = (struct flow *) arg;
130 
131  if (!dumping)
132  return;
133 
134  DEBUG_MSG(LOG_DEBUG, "fg_pcap_cleanup() called for flow %d", flow->id);
135  if (flow->pcap_dumper)
136  pcap_dump_close((pcap_dumper_t *)flow->pcap_dumper);
137  flow->pcap_dumper = NULL;
138 
139  if (flow->pcap_handle)
140  pcap_close((pcap_t *)flow->pcap_handle);
141  flow->pcap_handle = NULL;
142  dumping = false;
143 }
144 
155 static void* fg_pcap_work(void *arg)
156 {
157  /* note: all the wierd casts in this function are completely useless,
158  * execpt they cirumvent strange compiler warnings because of libpcap
159  * typedef woo's */
160 
161  /* signature of pthread_create() requires that all arguments must be
162  * passed by reference and cast to (void *) */
163  struct flow *flow = (struct flow *) arg;
164 
165  DEBUG_MSG(LOG_DEBUG, "fg_pcap_thread() called for flow %d", flow->id);
166 
167  /* make sure all resources are released when finished */
168  pthread_cleanup_push(fg_pcap_cleanup, (void*) flow);
169 
170  struct addrinfo *ainf = NULL;
171  int rc = getaddrinfo(flow->settings.bind_address, NULL, NULL, &ainf);
172  if (rc) {
173  logging(LOG_WARNING, "getaddrinfo() failed (%s). Eliding "
174  "packet capture for flow", gai_strerror(rc));
175  goto remove;
176  }
177 
178  /* find appropriate (used for test) interface to dump */
179  bool found = false;
180  pcap_if_t *d = NULL;
181  for (d = alldevs; d; d = d->next) {
182  for (pcap_addr_t *a = d->addresses; a; a = a->next) {
183  if (!a->addr)
184  continue;
185  if (sockaddr_compare(a->addr, ainf->ai_addr)) {
186  DEBUG_MSG(LOG_NOTICE, "pcap: data connection "
187  "inbound from %s (%s)", d->name,
188  fg_nameinfo(a->addr,
189  sizeof(struct sockaddr)));
190  found = true;
191  break;
192  }
193  }
194  if (found)
195  break;
196  }
197 
198  if (!found) {
199  logging(LOG_WARNING, "failed to determine interface for data "
200  "connection. No pcap support");
201  goto remove;
202  }
203 
204  flow->pcap_handle = (struct pcap_t *)pcap_open_live(
205  d->name, PCAP_SNAPLEN, PCAP_PROMISC,
206  0, errbuf); /* 0 = no read timeout */
207 
208  if (!flow->pcap_handle) {
209  logging(LOG_WARNING, "failed to init pcap on device %s: %s",
210  d->name, errbuf);
211  goto remove;
212  }
213 
214  uint32_t net = 0, mask = 0;
215  if (pcap_lookupnet(d->name, &net, &mask, errbuf) < 0) {
216  logging(LOG_WARNING, "pcap: netmask lookup failed: %s", errbuf);
217  goto remove;
218  }
219 
220  /* we rely on a non-blocking dispatch loop */
221  if (pcap_setnonblock((pcap_t *)flow->pcap_handle, 1, errbuf) < 0) {
222  logging(LOG_WARNING, "pcap: failed to set non-blocking: %s",
223  errbuf);
224  goto remove;
225  }
226 
227  /* compile filter */
228  struct bpf_program pcap_program;
229  if (pcap_compile((pcap_t *)flow->pcap_handle,
230  &pcap_program, PCAP_FILTER, 1, mask) < 0) {
231  logging(LOG_WARNING, "pcap: failed compiling filter '%s': %s",
232  PCAP_FILTER, pcap_geterr((pcap_t *)flow->pcap_handle));
233  goto remove;
234  }
235 
236  /* attach filter to interface */
237  if (pcap_setfilter((pcap_t *)flow->pcap_handle,
238  &pcap_program) < 0) {
239  logging(LOG_WARNING, "pcap: failed to set filter: %s",
240  pcap_geterr((pcap_t *)flow->pcap_handle));
241  goto remove;
242  }
243 
244  /* generate a nice filename */
245  char *dump_filename = NULL;
246 
247  /* dir and prefix */
248  if (dump_dir)
249  asprintf_append(&dump_filename, "%s", dump_dir);
250  if (dump_prefix)
251  asprintf_append(&dump_filename, "%s", dump_prefix);
252 
253  /* timestamp - we need to use the thread-safe version ctimenow_r() */
254  char timestamp[30] = "";
255  ctimenow_r(timestamp, sizeof(timestamp), false);
256  asprintf_append(&dump_filename, "%s", timestamp);
257 
258  /* hostname */
259  char hostname[128] = "";
260  if (!gethostname(hostname, sizeof(hostname)))
261  asprintf_append(&dump_filename, "-%s", hostname);
262 
263  /* interface and suffix */
264  asprintf_append(&dump_filename, "-%s.pcap", d->name);
265 
266  DEBUG_MSG(LOG_NOTICE, "dumping to \"%s\"", dump_filename);
267 
268  flow->pcap_dumper = (struct pcap_dumper_t *)pcap_dump_open(
269  (pcap_t *)flow->pcap_handle, dump_filename);
270  free(dump_filename);
271 
272  if (!flow->pcap_dumper) {
273  logging(LOG_WARNING, "pcap: failed to open dump file writing: %s",
274  pcap_geterr((pcap_t *)flow->pcap_handle));
275  goto remove;
276  }
277 
278  /* barrier: dump is ready */
279  pthread_barrier_wait(&pcap_barrier);
280 
281  for (;;) {
282  int rc = pcap_dispatch((pcap_t *)flow->pcap_handle, -1,
283  &pcap_dump, (u_char *)flow->pcap_dumper);
284 
285  if (rc < 0) {
286  logging(LOG_WARNING, "pcap_dispatch() failed. Packet "
287  "dumping stopped for flow %d", flow->id);
288  /* cleanup automatically called */
289  pthread_exit(0);
290  }
291 #ifdef DEBUG
292  struct pcap_stat p_stats;
293  pcap_stats((pcap_t *)flow->pcap_handle, &p_stats);
294 #endif /* DEBUG */
295  DEBUG_MSG(LOG_NOTICE, "pcap: finished dumping %d packets for "
296  "flow %d", rc, flow->id);
297  DEBUG_MSG(LOG_NOTICE, "pcap: %d packets received by filter for "
298  "flow %d", p_stats.ps_recv, flow->id);
299  DEBUG_MSG(LOG_NOTICE, "pcap: %d packets dropped by kernel for "
300  "flow %d", p_stats.ps_drop, flow->id);
301  if (rc == 0)
302  /* if no packets are received try if we should cancel */
303  pthread_testcancel();
304  }
305 
306 remove: ;
307 
308  pthread_cleanup_pop(1);
309 
310  pthread_barrier_wait(&pcap_barrier);
311  return 0;
312 }
313 
314 void fg_pcap_go(struct flow *flow)
315 {
316  if (!flow->settings.traffic_dump)
317  return;
318 
319  if (dumping) {
320  logging(LOG_WARNING, "pcap: dumping already in progress on "
321  "this host");
322  return;
323  }
324 
325  DEBUG_MSG(LOG_DEBUG, "called fg_pcap_go() for flow %d", flow->id);
326  dumping = true;
327 
328  int rc = pthread_create(&flow->pcap_thread, NULL, fg_pcap_work,
329  (void*)flow);
330 
331  /* barrier: dump thread is ready (or aborted) */
332  pthread_barrier_wait(&pcap_barrier);
333 
334  if (rc)
335  logging(LOG_WARNING, "could not start pcap thread: %s",
336  strerror(rc) );
337 }
338 
const char * fg_nameinfo(const struct sockaddr *sa, socklen_t salen)
Definition: fg_socket.c:374
const char * ctimenow_r(char *buf, size_t size, bool ns)
Returns the current wall-clock time as null-terminated string.
Definition: fg_time.c:47
Routines used by the Flowgrind daemon.
char sockaddr_compare(const struct sockaddr *a, const struct sockaddr *b)
Definition: fg_socket.c:389
#define PCAP_PROMISC
Definition: fg_pcap.c:70
char * dump_prefix
Definition: daemon.h:272
Debugging routines for Flowgrind controller and daemon.
struct pcap_dumper_t * pcap_dumper
Definition: daemon.h:163
void logging(int priority, const char *fmt,...)
Definition: fg_log.c:69
#define PCAP_FILTER
Definition: fg_pcap.c:67
void fg_pcap_go(struct flow *flow)
Start a tcpdump to capture traffic of the provided flow.
Definition: fg_pcap.c:314
int asprintf_append(char **strp, const char *fmt,...)
Definition: fg_string.c:98
struct flow_settings settings
Definition: daemon.h:83
#define DEBUG_MSG(LVL, MSG,...)
Print debug message to standard error.
Definition: debug.h:49
static pcap_if_t * alldevs
Definition: fg_pcap.c:79
int fg_pcap_init(void)
Initialize flowgrind&#39;s pcap library.
Definition: fg_pcap.c:84
Functions to manipulate strings used by Flowgrind.
char * dump_dir
Definition: daemon.h:273
static pthread_barrier_t pcap_barrier
Definition: fg_pcap.c:76
Missing pthread barrier implemenation for OS X.
static char errbuf[PCAP_ERRBUF_SIZE]
Definition: fg_pcap.c:73
int traffic_dump
Dump traffic using libpcap (option -M).
Definition: common.h:204
static bool dumping
Definition: fg_pcap.c:82
Routines used to manipulate socket parameters for Flowgrind.
pthread_t pcap_thread
Definition: daemon.h:161
Packet capture support for the Flowgrind daemon.
char bind_address[1000]
The interface address for the flow (used by daemon).
Definition: common.h:183
static void * fg_pcap_work(void *arg)
Worker method performing actual packet capturing for the provided flow.
Definition: fg_pcap.c:155
void fg_pcap_cleanup(void *arg)
Cleanup method to be called after dumping of the specified flow has finished.
Definition: fg_pcap.c:125
int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned count)
Allocates resources required to use the barrier referenced by barrier.
Definition: fg_barrier.c:37
Definition: daemon.h:73
#define PCAP_SNAPLEN
Definition: fg_pcap.c:64
Object for barrier synchronization.
Definition: fg_barrier.h:40
int id
Definition: daemon.h:75
int pthread_barrier_wait(pthread_barrier_t *barrier)
Synchronizes participating threads at the barrier referenced by barrier.
Definition: fg_barrier.c:80
Timing related routines used by Flowgrind.
struct pcap_t * pcap_handle
Definition: daemon.h:162